设计模式
单例模式
- 定义就是单例对象的类必须保证只有一个实例存在
- 适用于创建一个对象需要消耗过多资源的情况,例如访问数据库等资源
- 构造函数私有化(才不会让你有机会再创建一个对象)
- 静态方法或枚举返回单例对象
- 保证在多线程环境下对象也是单例的
- 单例类对象在反序列化是不会重新构建对象的
饿汉式
public class Singleton {
private final static Singleton instance = new Singleton();
//私有化构造器
private Singleton(){}
//共有静态方法,对外暴露获取单例对象
public static Singleton getInstance(){
return instance;
}
}
在静态对象声明时就已经初始化 getInstance -> 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方法是同步的来保证多线程中该变量也是单例。另一方面,getInstance每次同步导致了更多资源消耗。
getInstance -> synchronized判断 ->有则返回Instance,无则创建
Double Check Lock(DCL)
前者的改进,既能在需要时才初始化对象,又能保证线程安全,而且在对象初始化之后调用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 -> 判空 -> synchronized ->判空 -> new
由于语句new Singleton不是原子性操作 有可能先执行了对象初始化操作,再回到原线程执行构造函数,出错。就要使用volatile保证原子性。
静态内部类
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
/**静态内部类*/
private static class SingletonHolder{
public static final Singleton instance = new Singleton();
}
}
该种方式可以保证多线程并发的时候不会发生问题
第一次调用getInsance ->加载SingletonHolder类 ->new
加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
枚举单例
public enmu SingletonEnum {
INSTANCE;
}
简单粗暴,用于保证反序列化也不会重新生成新的实例。
使用容器实现单例模式
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);
}
}
可以管理多种类型的单例,使用时用同意的接口操作,LayoutInflater就是用的这种模式。
registerService->Map.containKey判断->初始化put对象进去->getService
优点
- 只存在一个实例,减少了内存开支,减少了系统的性能开销
- 避免对资源的多重占用
- 全局的访问点,优化和共享资源访问
缺点
- 没有接口,难扩展,只能修改代码
- 如果持有Context容易导致内存泄露(需要传递Context的话最好是Application Context)
网友评论