單例 序列化和readResolve 方法

2021-10-05 12:53:04 字數 3837 閱讀 7392

說到這個話題,我先丟擲單例的餓漢式寫法

單例:餓漢式

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...