【设计模式】——五问单例模式

0.什么是单例模式?
单例模式确保一个类只有一个实例,并提供一个全局访问点。


1.什么情况下使用单例模式?
当你需要确保程序中的一个类最多只有一个实例时。



2.如何实现单例模式?
 ①懒汉式:适用于对性能要求不高的情况。(不推荐)

缺点在于,第一次加载要初始化,反应比较慢; 每一次调用这个方法都需要同步,太过累赘。
   
   
public class Singleton{
private static Singleton uniqueInstance;
 
//其他有用的实例化的变量,synchronized关键字确保不会有两个线程同时进入这个方法
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
//其他有用的方法
}


②饿汉式:适用于经常创建并使用单例,或是在创建和运行时方面的负担不太繁重时。(推荐)
   
   
public class Singleton{
//在静态初始化器(static initialize)中创建单例,这段代码保证了线程安全
private static Singleton uniqueInstance = new Singleton();
//私有的构造器
private Singleton(){}
public static Singleton getInstance(){
//已经有实例,可以直接使用  
return uniqueInstance;
}
}
③双重检查加锁(不推荐)
首先检查实例是否已经创建,在没有创建的情况下才进行同步:即只有第一次会同步。
请注意,双重加锁检查不适用于1.4及更早版本的java,可能会出现失效。
  
  
public class Singleton{
//volatile关键词确保多个线程能正确处理uniqueInstance变量  
private volatile static Singleton uniqueInstance;
 
private Singleton(){}
 
public static Singleton getInsatance(){
//先检查实例是否存在,不存在才进入同步区块  
if(uniqueInstance == null){//避免不必要的同步
synchronized(Singleton.class){
if(uniqueInstance n== null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

④静态内部类(推荐)

   
   
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.sInstance;
}
 
/**
* 静态内部类
*/
private static class SingletonHolder{
private static final Singleton sInstance = new Singleton();
}
}
第一次加载Singleton类时并不会初始化sInstance,只有在第一次调用Singleton的getInstance方法才会导致sInstance被初始化,技能保证线程安全,也能保证单例对象唯一,是非常推荐的实现方式。

⑤枚举单例
优点:线程安全、任何情况下都是一个单例。不仅能有字段也能有自己的方法。
【枚举不受反序列化的影响。】
   
   
public enmu SingletonEnum{
INSTANCE;
public void doSomething(){
System.out.println("do sth");
}
}
其他实例中如果要防止单例对象在被反序列化时重新生成对象,需要加入以下方法:
   
   
private Object readResolve() throw ObjectStreamException{
return sInstance;
}
 
另外反射也是可以破坏单例的:
    
    
import java.lang.reflect.Constructor;
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public Singleton getInstance() {
return INSTANCE;
}
public static void main(String[] args) throws Exception {
// 反射机制破坏单例模式
Class clazz = Singleton.class;
Constructor c = clazz.getDeclaredConstructor();
// 反射机制使得private方法可以被访问!!!
c.setAccessible(true);
// 判断反射生成的对象与单例对象是否相等
System.out.println(Singleton.INSTANCE == c.newInstance());
}
}
 而枚举能解决这两个问题。
而枚举不好的一点就在于:
枚举类内存占用上是静态变量的两倍以上,所以在Android中要尽可能的避免这种写法
如果你的程序不是大量采用枚举,那么这种性能的体现是很小的,基本不会受到影响,不用特别在意。当然如果你的App出现了性能问题,理论上这个地方就是一个可以优化的性能优化点。、
关于枚举是如何优化的:
⑥使用容器实现单例模式
    
    
public class SingletonManager{
private static Map<String,Object> objMap = new HashMap<String,Object>();
 
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获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
 
最后,不管采取何种方案,请时刻牢记单例的三大要点:
线程安全
延迟加载
序列化与反序列化安全

3.单例模式能否继承
构造器是私有的,不能用私有构造器来扩展类,如果把构造器改成public或是protected就不能算是真正的”单例“了。

4.全局变量和单例模式的比较
全局变量可以提供全局访问,但是不能保证只有一个实例。这就是全局变量的局限性。


参考资料:
单例模式的一些注意点
让我们来破坏单例模式
《Android源码设计模式解析与实战》
《HeadFirst 设计模式》
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值