在學習單例的時候,看到這樣一句話在序列化|反序列化過程中,單例模式是會打破的
,今天就來一探究竟。
環境搭建
惡漢
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()
方法來實現,
解開前文惡漢
中封印的代碼: