单例设计模式-序列化破坏单例模式
最后更新于:2022-08-15 13:36:07
问题
使用单例模式,生成的一个单例,序列化成一个文件,然后再从文件中取出来,那这两个对象还是同一个对象么?
代码
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
测试
public class Test {
static String FILE_NAME = "singleton_file";
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
oos.writeObject(hungrySingleton);
File file = new File(FILE_NAME);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
HungrySingleton newInstance = (HungrySingleton) ois.readObject();
System.out.println(newInstance == hungrySingleton);
System.out.println(newInstance);
System.out.println(hungrySingleton);
}
}
结果
false
com.design.pattern.creational.singleton.HungrySingleton@16b98e56
com.design.pattern.creational.singleton.HungrySingleton@330bedb4
从结果来看,违背了单例模式的一个初衷,通过序列化和反序列化拿到了不同的对象。
解决方法
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
private Object readResolve() {
return hungrySingleton;
}
}
结果
true
com.design.pattern.creational.singleton.HungrySingleton@330bedb4
com.design.pattern.creational.singleton.HungrySingleton@330bedb4
原理
- 进入ObjectInputStream.readObject中
- 此处调用了readeObject0(),继续进去,其中有个switch,开始对应的类型,因为我们是object,所以继续往下看,进入readOrdinaryObject方法
- 此方法最终返回了一个obj,这里做了一个判断,如果方法desc.isInstantiable()为true,则返回desc.newInstance() ,否则返回null
- 继续查看desc.isInstantiable()方法,这里方法比较简单。查看注释,简单翻译就是:
如果一个类可以在运行时被实例化,那么该方法就会返回true
serializable: 可序列化
externalizable:定制序列化,需要序列化哪些字段都可以通过这个方法来定制
- 因为单例类实现了serializable,所以desc.isInstantiable()会返回true;
- 因为返回true,会执行desc.newInstance(),反射出一个对象,既然时反射出来的,所以一定和原本的对象是不同的,这也就解释了序列化和反序列化把单例模式破坏了。然后继续往下看,看到有如下判断
- 进入hasReadResolveMethod,看到如下简单方法,直接查看注释
如果一个类实现了serializable 或externalizable 且有readResolve方法,则返回true
- 因为代码满足以上条件,所以继续往下,进入invokeReadResolve方法,可以看出此方法就是哦通过反射调用readResolve方法,然后返回
- 在此类中搜索readResolve,可以知道定义readResolve方法名称的地方
- 至此,可以知道反序列化的时候,虽然生成了新的对象,只是没有返回,返回的还是原本的单例对象。所以结果还是同一个对象