设计模式之单例模式
单例模式是应用最广的设计模式之一,也可能是大多数程序员所了解的第一种设计模式(是我)
介绍
使用场景
- 很多时候我们需要一个全局对象,可以在各个activity中被调用,比如在Activity1中改变该对象的成员变量值,可以在ActivityB中得以使用
↑太菜了只这么用过↑
作用/好处
- 确保某个类的实例只有一个,并向整个系统提供这个实例
- 全局单例对象有助于开发者协调整体行为
- 对于创建时消耗大量资源的对象,可避免重复创建
实现
实现单例模式的关键点主要有以下几点
- 构造函数不对外开放,使用private关键字
- 通过静态方法或者枚举返回单例类的对象
- 确保单例类的对象只有一个
饿汉式
public class Singleton {
private final static Singleton instance = new Singleton();
//私有化构造器
private Singleton(){}
//共有静态方法,对外暴露获取单例对象
public static Singleton getInstance(){
return instance;
}
}
在声明静态对象时就已经初始化,如果初始化了却没有使用单例对象,则造成了不必要的内存开销。
懒汉式
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
与饿汉式不同的地方在于单例对象初始化的时机
并且getInstance()方法的synchronized关键字,保证了多线程情况下单例对象的唯一
相对的,每次调用getInstance()都会进行同步,消耗不必要的资源
双检锁/双重校验锁
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(null == instance){
synchronized(Singleton.class){
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
在getInstance()方法中对instance进行了两次判空
第一次判断是为了判断不必要的同步
第二次判断是为了在多线程的情况下创建实例(比如线程1和线程2都判空,然后都尝试获取monitor创建instance,如果不加第二次判空,线程1创建完了instance,线程2又会创建一次)
同时instance对象前面还添加了volatile关键字,保证instance的可见性
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
第一次加载Singleton类时并不会初始化instance
只有在第一次调用getInstance()方法时会初始化
第一次调用getInstance()方法导致虚拟机加载SingletonHolder类,确保线程安全
也能确保单例对象的唯一性,同时也延迟了单例对象的实例化,所以推荐使用这种实现方式
枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
写法简单,线程安全,并且在任何情况下都是一个单例
使用容器实现单例模式
public class SingletonManager{
private static Map<String,Object> objMap = new HashMap<>();
private SingletonManager(){}
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);
}
}
程序的初始将多种单例类型注入到一个统一的管理类中,使用时根据Key获取对应类型的对象
这种方式使得我们可以管理多种类型的单例,并且在使用时可以统一的接口进行操作
降低了使用成本,同时隐藏了具体实现并降低了耦合度
之前只了解了下饿汉式,懒汉式,双重校验锁的单例模式写法,结果有次看到前辈写的代码里的枚举式单例模式硬是不认识,😓,由此想着把其他单例模式的写法也都了解下