天天看点

设计模式|序列化、反序列化对单例的破坏、原因分析、解决方案及解析

目录

序列化、反序列化对单例的破坏

原因分析

解决方案及解析

序列化、反序列化对单例的破坏

单例模式是工作中高频使用的设计模式之一。单例模式可以确保内存中单例类只有一个实例,有效的减少了内存的开销,避免了类的重复创建和销毁。

序列化意义是将实现序列化的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方法,并将获得的对象返回,所以序列化前后对象相同!阻止了单例被破坏

设计模式|序列化、反序列化对单例的破坏、原因分析、解决方案及解析

文章内容参考自慕课网                   

设计模式学习友情链接:

单例模式的懒汉式为什么是线程不安全的,懒汉式如何实现线程安全

设计模式【创建型模式】

这位小可爱,如果觉得文章不错,请关注或点赞    (-__-)谢谢

设计模式|序列化、反序列化对单例的破坏、原因分析、解决方案及解析

继续阅读