天天看點

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

目錄

序列化、反序列化對單例的破壞

原因分析

解決方案及解析

序列化、反序列化對單例的破壞

單例模式是工作中高頻使用的設計模式之一。單例模式可以確定記憶體中單例類隻有一個執行個體,有效的減少了記憶體的開銷,避免了類的重複建立和銷毀。

序列化意義是将實作序列化的Java對象轉換成位元組序列 ,這些位元組序列可以被儲存在磁盤上,或者通過網絡傳輸。以備以後重新恢複成原來的對象。

對于單例類使用序列化、反序列化操作時,會破壞單例(序列化前的對象和反序列化後得到的對象記憶體位址不同),示範如下:

import java.io.*;

public class LazySingleTon implements Serializable {
    private LazySingleTon(){
    }
    public static  LazySingleTon getInstance(){
        return  InnerClass.lazySingleTon;
    }
    private static class InnerClass{
        private  static LazySingleTon lazySingleTon = new LazySingleTon();
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(LazySingleTon.getInstance());
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        LazySingleTon newInstance = (LazySingleTon) ois.readObject();
        //判斷是否是同一個對象
        System.out.println(newInstance);
        System.out.println(LazySingleTon.getInstance());
        System.out.println(newInstance == LazySingleTon.getInstance());
    }
 }
           

運作結果:

[email protected]
[email protected]
false
           

運作結果顯示通過序列化前後對象不同,表明單例已經被破壞了

原因分析

以不求甚解的姿勢對底層源碼進行分析一波

從ois.readObject()這個方法為入口,即ObjectInputStream類的readObject方法

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

找到readObject0方法中的switch片段,判斷反序列化對象類型,此時對象類型是Object

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

傳回值會調用readOrdinaryObject方法,readOrdinaryObject方法中的三目允許算符判斷了對象是不是可執行個體化的,如果是可執行個體化的會通過newInstance()方法反射執行個體化一個新的對象,是以序列化前的對象和反序列化後得到的對象不同!

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

解決方案及解析

解決方案是在單例類中加一個readResolve方法

public class LazySingleTon implements Serializable {
        //其他方法,略

        /**
         * 解決序列化、反序列化破壞單例
         * @return
         */
        public Object readResolve(){
            return getInstance();
        }
     }
           

再次運作main方法輸出:

[email protected]
[email protected]
true
           

可以看到這次序列化前後對象一緻,單例沒有被破壞

那為什麼加一個readResolve方法就能阻止單例被破壞呢?

在剛才分析的readOrdinaryObject方法有調用hasReadResolveMethod的判斷,這個方法是驗證目标類是否包含一個方法名為readResolve的方法,如果有就執行desc.invokeReadResolve,通過反射調用單例類的LazySingleTon的readResolve方法,即我們剛才加的readResolve方法,并将獲得的對象傳回,是以序列化前後對象相同!阻止了單例被破壞

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

文章内容參考自慕課網                   

設計模式學習友情連結:

單例模式的懶漢式為什麼是線程不安全的,懶漢式如何實作線程安全

設計模式【建立型模式】

這位小可愛,如果覺得文章不錯,請關注或點贊    (-__-)謝謝

設計模式|序列化、反序列化對單例的破壞、原因分析、解決方案及解析

繼續閱讀