序列化、反序列化破坏单例模式

427 阅读2分钟

序列化、反序列化破坏单例模式

我们现在使用静态内部类的方式来创建一个单例类(其他模式同理,除了枚举):

//Singleton.class
//私有构造方法
private Singleton() {}
​
//定义一个静态内部类
private static class SingletonHolder {
    //在内部类中声明并初始化外部类对象
    private static final Singleton INSTANCE = new Singleton();
}
​
//提供getter
public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
}

现在我们在测试类中来尝试破坏这个单例模式

定义一个向桌面文件中写入数据(对象)的方法

public static void writeObject2File() throws Exception {
    //1、获取Singleton对象
    Singleton instance = Singleton.getInstance();
    //2、创建对象输出流对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\User\Hexing\Desktop\a.txt"));
    //3、写对象
    oos.writeObject(instance);
    //4、释放资源
    oos.close();
}

现在在main方法中直接调用该方法就会报尚未序列化的错误(NotSerializableException)

这就需要Singleton类实现一个序列化接口:

public class Singleton implements Serializable

现在运行weiteObject2File方法就可以成功创建txt文件,并将对象进行写入

有了写入操作,我们再写一个读取操作:

public static void readObjectFromFile() throws Exception {
    //1、创建对象输入流对象
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\User\Hexing\Desktop\a.txt"))
    //2、读取对象并打印
    Singleton instance = (Singleton)ois.readObject();
    //观察这里打印结果,若两次调用打印输出的不同,则说明是不同的对象
    System.out.println(instance);
    //3、释放资源
    ois.close();
}

我们进行两次调用:

readObjectFromFile()
readObjectFromFile()

得到结果:

com.hexiaoxing.pattern.singleton.demo.Singleton@118181a8
com.hexiaoxing.pattern.singleton.demo.Singleton@4d674592

由此可见,两次输出的实例并不相同,所以两次得到的是两个对象,这样就成功破坏了单例模式。

解决方案

想要解决序列化、反序列化对单例模式的破坏,我们需要在Singleton类中添加readResolve()方法,在其被反序列化时调用,返回这个方法的值(对象)。

此前我们没有定义这个方法,返回的就是新new出来的对象,这种破坏方式的原理也在此

//readResolve方法
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
    return SingletonHolder.INSTANCE;
}

这样做的原理是在输入流对象的readObject方法的底层实现中,如果发现输入的是对象类型, 则会调用readOrdinaryObject方法,该方法中会自动执行hasReadResolveMethod方法,来判断类中是否有readResolve方法,如果有,则会去执行invokeReadResolve方法来获取类中专门提供的实例。