`
guitar427
  • 浏览: 4559 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

第3条--用私有构造器或者枚举类型强化Singleton属性

 
阅读更多

 

学习这条首先要了解什么是Singleton,Singleton就是仅仅被实例化一次的类,我们先来看一个常见的单例:

public class Singleton {
    public final static Singleton INSTANCE = new Singleton();
    
    private Singleton(){
        
    }
}

 

这样的单例其实并不能完全保证该类只被实例化一次,攻击者可以通过反射获得私有的构造器,并执行setAccessible(true)方法使其能够被访问,这就破坏了单例只能被实例化一次的特性。解决这个问题就是在私有的构造器中判断被执行的次数,不是第一次执行时候就抛出异常。

还有一种情况是有的单例需要被序列化,即实现java.io.Serializable接口。单例的对象被反序列化时就会创建新的实例。

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.txt"));
oos.writeObject(Singleton.INSTANCE);      
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.txt"));
Singleton sin = (Singleton)ois.readObject();

 
这里的sin就是一个新的实例,解决这个问题需要将单例中所有非基本类型的域都加transient修饰符,表明这些域都不需要被序列化,同时要在类中加入readResolve方法返回唯一的实例:

private Object readResolve(){
  return INSTANCE;
}

 

这样就能保证反序列化得到的对象也是那个唯一的实例,至于为什么会在后面序列化的章节专门有一条做阐释。

通过上面的分析过程,大家会发现实现一个完美的单例太复杂了,幸运的是,JDK1.5之后,我们有了更好的方式来实现单例,那就是枚举类型:

public enum EnumSingleton{
  INSTANCE;
}

 
这种方法再简单不过了,不需要考虑反射攻击的情况,而且无偿地提供了序列化机制,绝对防止多次实例化,所以在以后实现单例的时候优先地考虑枚举吧!

分享到:
评论
1 楼 iamhere2012 2013-05-06  
把枚举用作单实例,没这么玩过,学习了。果然没有查到枚举如何进行反射。

相关推荐

Global site tag (gtag.js) - Google Analytics