java enum实现的单例
为了防止通过反序列化得到多个对象,EJ提倡使用enum实现单例:关于枚举的对象为什么可以反序列化:可以看Enum类的如下方法:
/** * prevent default deserialization */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("can't deserialize enum"); } private void readObjectNoData() throws ObjectStreamException { throw new InvalidObjectException("can't deserialize enum"); }
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class Test {public static void main(String[] args) throws Exception {Singleton d1 = Singleton.INSTANCE;d1.setName("a fucker.");System.out.println(d1);FileOutputStream fos = new FileOutputStream("out.data");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(d1);fos.close();oos.close();FileInputStream fis = new FileInputStream("out.data");ObjectInputStream ois = new ObjectInputStream(fis);Object o = ois.readObject();fis.close();ois.close();Singleton d2 = (Singleton)o;System.out.println(d2);System.out.println(d1 == d2);}}enum Singleton implements Serializable {INSTANCE;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "[" + name + "]";}}
因为一个enum常量(这里是INSTANCE)代表了一个enum的实例,enum类型只能有这些常量实例。标准保证enum常量(INSTANCE)不能被克隆,也不会因为反序列化产生不同的实例,想通过反射机制得到一个enum类型的实例也不行的。
如果用一般方式写单例模式,该单例类如果实现了Serializable接口,则必须添加readResolve()方法,当然我认为按照Enum类的设计在 readObject(ObjectInputStream in)中抛出异常也可以有效防止反序列化:public class Singleton {private static volatile Singleton st;private Singleton(){};public static Singleton getInstance() {if (null == st) {synchronized (Singleton.class) {if (st == null) {st = new Singleton();return st;}}}return st;}/** * 反序列化时内存Hook这段代码 * @return */private Object readResolve() {return st;}}
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException; ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
这两个方法可以理解为序列化和反序列化过程的入口和出口。writeReplace()返回的对象,就是要被序列化的对象,我们有机会在序列化前把这个对象给换成我们确定好的那个(如果不是“故意捣乱”,暂时没想到有什么用);而readResolve()方法就是在反序列化完成得到对象前,把这个对象给换成我们确定好的那个。
明白了吧?为了防止有人恶意通过序列化的机制破坏定义好的单例,我们就需要自己实现readResolve()方法,把单例定义的唯一实现在这个方法中返回。
一般单例处理方法:http://www.blogjava.net/dreamstone/archive/2006/11/04/79026.html
恶汉式的单例也有问题:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/index.html
页:
[1]