工作中我们封装Util或封装SDK都离不开单例模式,为什么要用单例模式下面是我的个人理解。
一些常用的工具类,由于其使用频率较高,如果每次需要使用时都新建一个对象,不仅会占用大量内存,还会导致系统负载增加,影响应用程序的性能。使用单例模式,可以在应用程序启动时就创建一个实例,直到应用程序结束时才销毁该实例,这样就能保证该工具类在整个应用程序中只有一个实例对象被使用,从而提高程序的效率和性能。
单例模式属于创建类模式。单例的核心定义是确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
//单例类.
public class Singleton {
private Singleton() {//构造方法为private,防止外部代码直接通过new来构造多个对象
}
private static final Singleton single = new Singleton(); //在类初始化时,已经自行实例化,所以是线程安全的。
public static Singleton getInstance() { //通过getInstance()方法获取实例对象
return single;
}
}
//单例类
public class Singleton {
private Singleton() {
}
private static Singleton single = null;
public static Singleton getInstance() {
if (single == null) {
single = new Singleton(); //在第一次调用getInstance()时才实例化,实现懒加载,所以叫懒汉式
}
return single;
}
}
//单例类
public class Singleton {
private Singleton() {
}
private static Singleton single = null;
public static synchronized Singleton getInstance() { //加上synchronized同步
if (single == null) {
single = new Singleton();
}
return single;
}
}
getInstance()
方法上加上 synchronized 关键字即可实现线程安全。getInstance()
方法时都需要进行加锁和解锁操作,会影响程序的性能。public class Singleton1 {
//第一点:首先private是必须的,保证无法通过类名加点访问,可能是空值
//第五点:因为以上几点原因,这里的static也是必须的
//第十点:volatile 保证可见性,一旦修改成功,其他线程第一时间都能看到
private static Singleton1 volatile instance = null;
//第二点:这里的private是必须的,构造器必须私有,保证其他类无法new出对象实例
private Singleton1() {
}
//第三点:要对外提供访问接口,因此获取实例的方法必须public型
//第四点:因为其他类无法通过构造对象实例然后加点访问,只能通过类名加点访问,故必须用static
public static Singleton1 getSingleton1Instance() {
//第六点:第一次check
if (null == instance) {
//第七点:因为这里可能多个线程都会判断条件满足并进入,所以这里要加锁
synchronized (Singleton1.class) {
//第八点:判断条件进入的线程最终都会获得锁,因此这里进行第二次检查
if (null == instance) {
instance = new Singleton1();
}
}
}
return instance;
}
}
public class Singleton {
//第一点:构造器私有,避免外部使用new 构造该对象
private Singleton(){
}
//第四点:内部类用private修饰,表示其外部类私有,只有其外部类可以访问
//第五点:内部类用static修饰,表示该类属于外部类本身,而非外部类对象,而且外部类方法是静态的,所以内部类也必须要修饰成static
private static class SingletonHandler{
//第六点:final 表示该引用指向的地址赋值一次之后地址不能被改变,而且在类加载的准备阶段已经将对象创建并将地址赋给
singleton,注意,final是必须的,如果不用final,也就是说还可以new 一个对象,并将对象引用赋给singleton,这就不能保证
唯一性
private final static Singleton singleton = new Singleton();
}
//第二点:提供外部访问单例对象的唯一途径,所以必须是pulic类型方法
//第三点:因为外部不能构造对象,只能通过Singleton.getSingletonInstance访问该方法,所以该方法必须用static修饰
public static Singleton getSingletonInstance(){
return SingletonHandler.singleton;
}
}
//单例管理类
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);//添加单例
}
}
public static Object getService(String key) {
return objMap.get(key);//获取单例
}
}
public enum Singleton {
INSTANCE;
// 添加需要实现的方法
public void doSomething() {
// 执行某些操作
}
}
public class TestSingleton {
public static void main(String[] args) {
// 获取单例实例
Singleton singleton = Singleton.INSTANCE;
// 调用实例方法
singleton.doSomething();
}
}
Serializable
接口,因此它的实例可以被序列化和反序列化。同时,由于枚举类的实例是在枚举类型中定义的,反序列化时会通过调用 valueOf()
方法来获取实例,因此可以保证序列化和反序列化的一致性和安全性。 Constructor con = Singleton.class.getDeclaredConstructor();
con.setAccessible(true);
// 通过反射获取实例
Singleton singeton1 = (Singleton) con.newInstance();
Singleton singeton2 = (Singleton) con.newInstance();
System.out.println(singeton1==singeton2);//结果为false,singeton1和singeton2将是两个不同的实例
public class Singleton {
private static boolean flag = true;
private static Singleton single = null;
private Singleton() {
if (flag) {
flag = !flag;
} else {
throw new RuntimeException("单例模式被破坏!");
}
}
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
public class Singleton implements Serializable {
private Singleton() {
}
private static final Singleton single = new Singleton();
public static Singleton getInstance() {
return single;
}
private Object readResolve() throws ObjectStreamException {//重写readResolve()
return single;//直接返回单例对象
}
}
单例对象一旦被创建,它的生命周期会和应用程序一样长,如果该对象占用的资源过多,会导致系统的负载变高,因此需要注意控制单例对象的资源占用情况。
public class MySingleton {
private static MySingleton instance = null;
private int[] bigArray; // 一个大数组,占用大量内存
private MySingleton() {
// 初始化 bigArray
bigArray = new int[1000000];
}
public static synchronized MySingleton getInstance() {
if (instance == null) {
instance = new MySingleton();
}
return instance;
}
public void useBigArray() {
// 对 bigArray 进行操作,模拟资源占用
for (int i = 0; i < bigArray.length; i++) {
bigArray[i] = i;
}
}
}
在这个示例中,MySingleton 类是一个单例模式类,它包含一个 bigArray 数组成员变量,该数组占用了较多的内存空间。该类通过 getInstance() 方法获取单例对象,并且在构造函数中初始化了 bigArray。useBigArray() 方法模拟了对资源的占用。
在实际开发中,如果我们使用该类的实例时频繁地调用 useBigArray() 方法,可能会导致系统的负载变高,因为该方法占用了大量的内存空间。为了避免这种情况,我们可以使用一些方法来控制资源的占用,例如: