在学习单例的时候,看到这样一句话在序列化|反序列化过程中,单例模式是会打破的
,今天就来一探究竟。
环境搭建
恶汉
public class HungrySingleton implements Serializable {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return INSTANCE;
}
/* private Object readResolve(){ return INSTANCE;} */
}
test-case
private static void destory_hungry() throws IOException, ClassNotFoundException {
HungrySingleton instance = HungrySingleton.getInstance();
Path path = Paths.get("HungrySingleton_001");
if(Files.exists(path)) {
Files.delete(path);
}
//序列化到指定文件
File file = Files.createFile(path).toFile();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(instance);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
HungrySingleton instance2 = (HungrySingleton) ois.readObject();
//打印false ,证明存在两个对象 ,说明单例模式被打破
System.out.println(instance == instance2);
}
源码分析
反序列化方法的入口ois.readObject()
,从这里跟踪代码分析:
//java.io.ObjectInputStream
//1.
public final Object readObject()
throws IOException, ClassNotFoundException{
try {
//2.
Object obj = readObject0(false);
return obj;
} finally {
}
}
//2.
private Object readObject0(boolean unshared) throws IOException {
try {
switch (tc) {
//略.......
case TC_OBJECT:
//3.1 readOrdinaryObject(unshared)
return checkResolve(readOrdinaryObject(unshared));
//略.......
}
} finally {
}
}
//3.1
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
Object obj;
try {
//执行无参构造方法,实例化一个对象
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
}
//desc.hasReadResolveMethod():判断是否定义readResolve方法
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
//如果有则 执行该方法;
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// obj = rep; 覆盖obj原有值
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
解决
如何防止单例模式被破坏呢,可以通过定义readResolve()
方法来实现,
解开前文恶汉
中封印的代码: