单例设计模式-序列化破坏单例模式

问题
使用单例模式,生成的一个单例,序列化成一个文件,然后再从文件中取出来,那这两个对象还是同一个对象么?

代码

public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

测试

public class Test {
    static String FILE_NAME = "singleton_file";

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        HungrySingleton hungrySingleton = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
        oos.writeObject(hungrySingleton);

        File file = new File(FILE_NAME);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        HungrySingleton newInstance = (HungrySingleton) ois.readObject();

        System.out.println(newInstance == hungrySingleton);
        System.out.println(newInstance);
        System.out.println(hungrySingleton);
    }
}

结果

false
com.design.pattern.creational.singleton.HungrySingleton@16b98e56
com.design.pattern.creational.singleton.HungrySingleton@330bedb4

从结果来看,违背了单例模式的一个初衷,通过序列化和反序列化拿到了不同的对象。

解决方法

public class HungrySingleton implements Serializable {

    private final static HungrySingleton hungrySingleton;

    static {
        hungrySingleton = new HungrySingleton();
    }

    private HungrySingleton() {

    }

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }

    private Object readResolve() {
        return hungrySingleton;
    }

}

结果

true
com.design.pattern.creational.singleton.HungrySingleton@330bedb4
com.design.pattern.creational.singleton.HungrySingleton@330bedb4

原理

  1. 进入ObjectInputStream.readObject中

  1. 此处调用了readeObject0(),继续进去,其中有个switch,开始对应的类型,因为我们是object,所以继续往下看,进入readOrdinaryObject方法

  1. 此方法最终返回了一个obj,这里做了一个判断,如果方法desc.isInstantiable()为true,则返回desc.newInstance() ,否则返回null

  1. 继续查看desc.isInstantiable()方法,这里方法比较简单。查看注释,简单翻译就是:

如果一个类可以在运行时被实例化,那么该方法就会返回true
serializable: 可序列化
externalizable:定制序列化,需要序列化哪些字段都可以通过这个方法来定制

  1. 因为单例类实现了serializable,所以desc.isInstantiable()会返回true;

  1. 因为返回true,会执行desc.newInstance(),反射出一个对象,既然时反射出来的,所以一定和原本的对象是不同的,这也就解释了序列化和反序列化把单例模式破坏了。然后继续往下看,看到有如下判断

  1. 进入hasReadResolveMethod,看到如下简单方法,直接查看注释

    如果一个类实现了serializable 或externalizable 且有readResolve方法,则返回true

  1. 因为代码满足以上条件,所以继续往下,进入invokeReadResolve方法,可以看出此方法就是哦通过反射调用readResolve方法,然后返回

  1. 在此类中搜索readResolve,可以知道定义readResolve方法名称的地方

  1. 至此,可以知道反序列化的时候,虽然生成了新的对象,只是没有返回,返回的还是原本的单例对象。所以结果还是同一个对象

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注