欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 09:25:27
...

 

学习这条首先要了解什么是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;
}

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