Java 序列化 之 單例模式

序列化相關文章:
* Java 序列化 之 Serializable
* Java 序列化之 Externalizable

當我們使用Singleton模式時,應該是期望某個類的實例應該是唯一的,但如果該類是可序列化的,那麼發序列化後還會是單例的嗎?下面我們通過如下示例一來驗證一下:

示例一

User 類

User 類是單例模式,使用的餓漢模式,在類加載的時候就創建對象實例。

public class User implements Serializable {
    private static final long serialVersionUID = 3380014540967816490L;

    private String userName;
    private String password;

    private static User user = new User("zhangsan", "test");

    private User(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }
    public static User getInstance() {
        return user;
    }
    public String getUserName() {
        return userName;
    }
    public String getPassword() {
        return password;
    }

Test 類

測試類,把 User 的單例實例序列化後在反序列化。

public class Test{
    public static void main(String[] args) throws Exception {
        File file = new File("d:\\a.user");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(User.getInstance());


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        User user = (User) ois.readObject();
        System.out.println(user);

        if(user==User.getInstance()){
            System.out.println("同一個實例");
        }else{
            System.out.println("不同的實例");
        }
    }
}

執行結果如下:

輸出的結果:
User [userName=zhangsan, password=123456]
不同的實例

通過結果可以看出,單例模式的餓漢模式也無法確保對象實例是單例的。

那麼我們應該怎麼解決這個問題呢?

readResolve() 方法

public class User implements Serializable {

    private static final long serialVersionUID = 3380014540967816490L;

    private String userName;
    private String password;

    private static User user = new User("zhangsan", "123456");

    private User(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }
    public static User getInstance() {
        return user;
    }
    public String getUserName() {
        return userName;
    }
    public String getPassword() {
        return password;
    }
    public Object readResolve(){
        return getInstance();
    }
    @Override
    public String toString() {
        return "User [userName=" + userName + ", password=" + password + "]";
    }
}

我們在 User 類中添加了一個 readResolve() 方法,該方法直接返回單例中的示例。
然後在執行 Test.main() 方法
執行結果如下:

輸出的結果:
User [userName=zhangsan, password=123456]
相同的實例

無論是實現Serializable接口,或是Externalizable接口,當從I/O流中讀取對象時,readResolve()方法都會被調用到。實際上就是用readResolve()中返回的對象直接替換在反序列化過程中創建的對象。


想了解更多精彩內容請關注我的公衆號

本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8    
點擊這裏快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這裏快速進入GIT

發佈了132 篇原創文章 · 獲贊 79 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章