effective java笔记之单例模式与序列化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了effective java笔记之单例模式与序列化相关的知识,希望对你有一定的参考价值。

单例模式:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”

单例模式实现方式有多种,例如懒汉模式(等用到时候再实例化),饿汉模式(类加载时就实例化)等,这里用饿汉模式方法实现,也就是类加载就实例化,单例模式应用场景有很多,比如一个应用有一套窗口化界面,Servlet中只有一个实例,应用很广泛

package com.test;

public class Singleton {
    
    private Singleton() {}
    private static final Singleton INSTANCE = new Singleton();    //饿汉模式
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

单例模式基本上是每个java程序员都知道的,effective java第一章讲的是对象创建与销毁,看到单例模式在序列化中会失效时,这个是我之前所不知道的,于是查阅资料,并亲自验证一下,过程很简单,将上面的单例类实现Serializable接口

package com.test;

import java.io.Serializable;

public class Singleton implements Serializable{
    
    /**  
    * @Fields serialVersionUID : TODO
    */  
    private static final long serialVersionUID = -6367362518368424353L;

    private Singleton() {}
    private static final Singleton INSTANCE = new Singleton();    
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

是时候来验证一下了

package com.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Singleton before = Singleton.getInstance();
        
        //序列化,将对象写入到文件
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\objectFile"));
        oos.writeObject(before);
        oos.close();
        
        //反序列化,读取文件,还原成对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\objectFile"));
        Singleton after = (Singleton) ois.readObject();
        ois.close();
        
        //验证结果
        System.out.println(before == after);
    }
}

输出:false

这说明一旦对象被序列化了,就破坏了单例模式,因为重新读取的对象已经不是之前的对象,它重新生成了一个!

那么问题来了,如何解决单例模式与序列化的矛盾?

effective给出了解决方法,只需添加一个readResolve方法,即

package com.test;

import java.io.Serializable;

public class Singleton implements Serializable{
    
    /**  
    * @Fields serialVersionUID : TODO
    */  
    private static final long serialVersionUID = -6367362518368424353L;

    private Singleton() {}
    private static final Singleton INSTANCE = new Singleton();    
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    private Object readResolve() {
        return INSTANCE;
    }
}

再运行一下之前的测试类,发现结果又变成true了,说明反序列化单例模式没有被破坏!关于其中的原理可以参阅这篇博客 http://www.hollischuang.com/archives/1144

终极解决方法,枚举类!

使用起来极其简单,只需要

package com.test;

import java.io.Serializable;

public enum Singleton implements Serializable{
    INSTANCE;
}

然后再测试一下

package com.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Singleton before = Singleton.INSTANCE;
        
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\objectFile"));
        oos.writeObject(before);
        oos.close();
        
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\objectFile"));
        Singleton after = (Singleton) ois.readObject();
        ois.close();
        
        //验证结果
        System.out.println(before == after);
    }
}

输出结果为true

以上是关于effective java笔记之单例模式与序列化的主要内容,如果未能解决你的问题,请参考以下文章

《JAVA与模式》之单例模式

《JAVA与模式》之单例模式

JAVA设计模式之单例模式

Java 设计模式之单例学习与掌握

软件设计模式之单例模式

Java设计模式之单例模式与工厂模式