学习这条首先要了解什么是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;
}
这种方法再简单不过了,不需要考虑反射攻击的情况,而且无偿地提供了序列化机制,绝对防止多次实例化,所以在以后实现单例的时候优先地考虑枚举吧!