說到這個話題,我先丟擲單例的餓漢式寫法
單例:餓漢式
public class hungrysingleton
private static final hungrysingleton hungry = new hungrysingleton();
public static hungrysingleton getinstance()
}
首先需讓hungrysingleton支援序列化, 修改hungrysingleton類
public class hungrysingleton implements serializable catch (ioexception e) catch (classnotfoundexception e) }}
執行結果
從執行結果可以看出,序列化破壞了單例,產生了多個例項。
那我們如何解決呢?
在hungrysingleton 類中新增 readresolve()方法就可以完美解決
public class hungrysingleton implements serializable
private static final hungrysingleton hungry = new hungrysingleton();
public static hungrysingleton getinstance()
// 我們新增的readresolve()方法
private object readresolve()
}
如上新增後,執行client我們對測試類,可以看到列印出true。
大家一定會有疑問,readresolve這個方法為啥可以解決序列化破壞單例的問題
readresolve()為啥就可以解決序列化破壞單例的問題呢?
源頭就在於我們測試類中的
objectinputstream ois = new objectinputstream(fis);
singleton1 = (hungrysingleton) ois.readobject();// 這句**是我們的入手點
進到objectinputstream#readobject()看原始碼,try第一句**就是
//返回的obj物件,就是objectinputstream的readobject0返回的物件。
object obj = readobject0(false);
進入objectinputstream#readobject0(),switch語句對列舉或者object類都有對應的序列化機制
重點**
case tc_enum:
// 這句**是針對列舉,單例中為啥列舉式最安全,就是看這行**,後續,小夥伴可以研讀研讀
return checkresolve(readenum(unshared));
case tc_object:
//我們的object 類
return checkresolve(readordinaryobject(unshared));
checkresolve:檢查物件,並替換
readordinaryobject:讀取二進位制物件
我們先進入readordinaryobject()方法
try catch (exception ex)
可以看到,readordinaryobject()方法是通過desc.isinstantiable() 來判斷是否需要new乙個物件,如果返回true,方法通過反射的方式呼叫無參構造方法新建乙個物件,否則,返回空。
那我們進入isinstantiable()方法,
boolean isinstantiable()
cons != null是判斷類的構造方法是否為空,我們大家應該知道,class類的構造方法肯定不為空,顯然isinstantiable()返回true,也就是說,一定會new 乙個物件,且被obj接收。
我們回到readordinaryobject()方法,檢視初始化完成後的操作。
我們要敲黑板了,這裡就是單例類中定義readresolve就可以解決問題的關鍵所在!
若"obj != null &&handles.lookupexception(passhandle) == null &&desc.hasreadresolvemethod()"這條語句返回true
就會呼叫object rep = desc.invokereadresolve(obj) 這條語句。
我們進入hasreadresolvemethod()方法
boolean hasreadresolvemethod()
"readresolvemethod != null "的判斷,我們深究進去,readresolvemethod是從**讀取進來的
/** class-defined readresolve method, or null if none */
// 定義readresolvemethod 的方法
private method readresolvemethod;
// 對readresolvemethod賦值, 通過反射獲得類中名為readresolve的方法
也就是說!
若目標類有readresolve方法,那就通過反射的方式呼叫要被反序列化的類中的readresolve方法,返回乙個物件,然後把這個新的物件複製給之前建立的obj(即最終返回的物件)。那被反序列化的類中的readresolve 方法裡是什麼?就是直接返回我們的單例物件。
再次貼上來,我們的單例類。
public class hungrysingleton implements serializable
private static final hungrysingleton hungry = new hungrysingleton();
public static hungrysingleton getinstance()
private object readresolve()
}
序列化與單例
當單例模式的類實現了系列化serializable介面,也可以通過反序列化來使它不再單例。我們的單例類 12 3 4 5 6 7 8 9 10 11 12 publicfinalclasssingletonimplementsserializable publicstaticsingleton ge...
單例設計模式 序列化破壞單例模式?
1 問題猜想,假如將乙個物件通過序列化放到乙個檔案後,再取出來看是否與本身相等?public class hungrysingleton implements serializable private hungrysingleton public static hungrysingleton get...
序列化和反序列化破壞單例模式的解決方法
我們知道單利模式可以確保在系統中只存在唯一例項,不過當序列化遇到單例時,裡邊就有了個問題 從記憶體讀出而組裝的物件破壞了單例的規則。為了解決這個問題提供一下兩種解決方案 單利類1 public class singleton implements serializable private stati...