我是架构师--设计模式-单例模式

来次面试吧?准备好没,GO!

 
  

 

  1.    问:自我介绍下吧。(开个玩笑。。。往下)请问你用过单例模式吗?什么是单例?  
  2. 答:用过啊,单例模式就是只创建一个实例。  
  3.    问:噢?那是单线程,还是多线程下都是呢?  
  4. 答:这个类在JVM里就一个实例(这样回答也许会更好)  
  5.    (注:通常面试官,到这里,就会让你写一个单例,当然我们不,你看这篇文章,就是不让你再网上找其他重复的资料了。)  
  6.    问:说说你用的场景吧  
  7. 答:........  
  8.    问:说说单例的几种类型?或者说何时对实例的初始化?  
  9. 答:........  
  10.    问:画过单例的类图吗?会画吗?  
  11. 答:(需要吗?)  
  12.    问:写个单例的实现吧?  

 类名:SingleTon

 实例:uniqueInstance 简称ust吧。(这里我插入一句编程规范:起名不要吝惜把实例代表的意思表达清楚,名字可稍微长点,这里就是想偷懒) 

实现1:

 
  
  1. public class Singleton{  
  2.   private final static Singleton ust = new Singleton();  
  3.   private Singleton() {}  
  4.   public static Singleton getInstance(){  
  5.      return ust;  
  6.   }  
  7. }  

注:这里顺便说明一下语法。通常final与static同时出现,习惯让final在前

分析: 

1. 这里需要加final吗? 是的,因为java反射,可以改变private描述的变量

2. 有书里把这种方式称为饿汉单例模式(另一个种叫懒汉单例),并且已经为人所接受

3. 在多线程方面表现出了他优势,不需要担心方法重复里延时创建带来的原子性(这样说难理解,其实就是出现两个或多个实例)

4. 这个,不符合我们习惯的 用到时再实例化的原则(不过这没关系。。。)

 看,其实你发现这还是个不错的单例,那么其实有个最好的实现,最好的实现:

单元素的枚举类型已经成为Singleton的最佳实践

即使面对复杂的序列化或者反射攻击,绝对防止多次实例化,还有他的简洁和优雅。

 ----好吧,面试结束了。 其实你对单例的理解还是不错的,而且你已经得到了最好的答案,有兴趣彻底玩转单例吗,继续听我唠叨。


    我们看看懒汉单例模式:

实现2:

 
  
  1. public class Singleton{  
  2.     // private final static Singleton ust = null;
  3. // 不该加final,这里明显有偷懒嫌疑,复制上面的例子,又测试不够,以后尽量避免类似问题。
  4. // 8月15日修正
  5. private static Singleton ust = null;
  6.      private Singleton(){}  
  7.      public staic Singleton getInstance(){  
  8. //建议null==ust的方式,能帮助更快的发现错误。
  9. //部分老程序员的习惯,其实许多IDE会发现些低级错误。  
  10.         if(ust==null){//A  
  11.              ust = new Singleton();//B  
  12.          }  
  13.        return ust;  
  14.      }  
  15. }   

分析:

1. 懒汉模式,做到了需要时创建实例

2. 他遇到了尴尬的问题,因为当两个线程分开运行到A,然后进入了if块,可能就创建了2个实例,草稿的是,你已经初始了一些数据。 

改进一下:

实现3: 

 
  
  1. public class Singleton{  
  2. private volatile static Singleton ust;  
  3. private Singleton(){}  
  4. public staic Singleton getInstance(){  
  5. synchronized(Singleton.class){  
  6.    if(ust==null){  
  7.     ustnew Singleton();  
  8.    }  
  9.  }  
  10. return ust;  
  11. }  
  12. }  

 分析:

  1. 如果你不理解synchronized 的位置,就不用单例模式这么多写法,不如学习基础

  2. volatile 确保ust被实例化后,多个线程正确处理。他失去了JVM必要的代码优化,如果不是多线程,就不要用

   3. 这个叫做 “双检查加锁”,单例最后一种方式


综合讨论会: 

 
  
  1. 小明: 单例目前一共谈到懒汉和饿汉两种,还有双检查加锁,最好的应该是单元素的枚举类型  
  2. 小刚: 是的,回答了开头说的几种单例,那么哪些场景应该用单例呢?  
  3. 小明: 我知道,有线程池,缓存,处理偏好设置,注册表,日志对象等等  
  4. 小刚: 对,我对java比较了解,我知道Runtime.getRuntime()。   
  5. C(为吗我叫C):我知道有java.lang.reflect.Proxy类   
  6. 小明:有什么共同点呢,为什么用?是遵循对象尽量少创建原则? 
  7. C:这是什么意思?   
  8. 小刚:这很简单,不过这说法有点问题。因为对象占内存,有要造成垃圾回收,GC的时候JVM可是只干这个麻烦事  
  9. 小明:是啊 
  10. 小刚:我想我知道,某些对象最好只有一个实例,多了会有问题产生。   
  11. C:什么问题?   
  12. 小刚:比如缓存,你从哪个实例里拿缓存呢?   
  13. 小明:是的啊。。。  

结束讨论会,总结一下吧。不,等等,还要补充两句: 
1. 单例模式定义:确保一个只有一个实例,并提供一个全局访问点

2. 如果getInstance()方法对应用程序不会额外负担,或者说影响不大,那写成怎样,其实没太大所谓。但是如果频繁运行,就要仔细考虑,因为一个同步,可能使得执行效率下降100倍

 

继续总结,还差个UML图呢,不妨在上个枚举的例子吧,枚举构造器默认私有吗? 

image 


枚举就算了,是不是默认构造器,自己研究下吧。。。呵呵

这回真总结了: 
1. 单例,有懒汉,饿汉,双检查加锁3种常见用法 
2. 单例模式,是因为如果多了,会造成数据遗漏等麻烦 
3. 最好的单例,单元素的枚举类型

 呵呵,其实,就这些,本来想先写工厂的,因为去面试,遇到某些对单例了解比较浅,解释起来费劲,于是先以单例开篇,请关注下篇工厂模式。










本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/yjplxq/931861,如需转载请自行联系原作者
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值