序列化的过程中单例是被破坏的
昨天遇到了一个面试题,网络传输中序列化后双重检查锁的单例模式还是单例的吗,触及到了知识盲区了,还是得好好学,记录一下。
首先,序列化过程中的单例模式是会被破坏的,因为序列化出来的对象与原单例对象不一样了。
话不多说,上代码。
public class SingletonDemo implements Serializable{
private static volatile SingletonDemo instance;
private SingletonDemo () {
}
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}
return instance;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
SingletonDemo instance1 = SingletonDemo.getInstance();
FileOutputStream fos = new FileOutputStream("temp");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance1);
FileInputStream fis = new FileInputStream("temp");
ObjectInputStream ois = new ObjectInputStream(fis);
SingletonDemo singletonDemo = (SingletonDemo) ois.readObject();
System.out.println(singletonDemo == instance1);
}
}
结果:
原因
那么为什么序列化出来的对象就不是单例的呢?
我们就得看看源码了,从ois.readObject()这个方法为入口,就是ObjectInputStream类的readObject()方法
找到readObject0方法中的switch片段,判断反序列化对象类型,此时对象类型是Object
返回值会调用readOrdinaryObject方法,readOrdinaryObject方法中的三目允许算符判断了对象是不是可实例化的,如果是可实例化的会通过newInstance()方法反射实例化一个新的对象,所以序列化前的对象和反序列化后得到的对象不同!
解决方法
解决方案是在单例类中加一个readResolve方法
加了readResolve方法后的运行结果
两个对象的引用比对是一致的,所以单例没有被破坏
那为什么加一个readResolve()方法就可以阻止单例被破坏呢?
在刚才分析的readOrdinaryObject方法有调用hasReadResolveMethod的判断,这个方法是验证目标类是否包含一个方法名为readResolve的方法,如果有就执行desc.invokeReadResolve,通过反射调用单例类的LazySingleTon的readResolve方法,即我们刚才加的readResolve方法,并将获得的对象返回,所以序列化前后对象相同!阻止了单例被破坏