單例模式是如何被破壞的

在學習單例的時候,看到這樣一句話在序列化|反序列化過程中,單例模式是會打破的,今天就來一探究竟。

環境搭建

惡漢

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()方法來實現,
解開前文惡漢中封印的代碼:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章