单例类序列化

Shw*_*ani 0 java singleton serialization static deserialization

我的单例类是这样的:

public class SerializableSingleton implements Serializable {

    private static SerializableSingleton instance = new SerializableSingleton();

    private SerializableSingleton() {
        System.out.println("Constructor is being called");
    }

    public static SerializableSingleton getInstance() {
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在网上写到,当我们反序列化这个单例对象时,它将返回一个新实例,而不是前一个实例,为了解决这个问题,请使用该readResolve()方法。

但我的问题是——这怎么可能?当静态类成员无法序列化时,如何反序列化它呢?而且全网都这样?

由于单例对象是静态的:

private static SerializableSingleton instance = new SerializableSingleton();
Run Code Online (Sandbox Code Playgroud)

实例首先是如何被序列化的?

Joh*_*uhn 8

序列化绕过了语言中的很多东西,甚至做了一些普通反射不可能完成的事情。

当对象被序列化时,它的类名和所有实例字段(不是transient)都会写入流中。

当反序列化一个对象时,奇迹就发生了。

  • 首先,分配该类的一个新实例。
    为此,将调用第一个不可序列化超类的无参构造函数。跳过可序列化类(和可序列化超类)的构造函数。如何?魔法
  • 然后设置可序列化类的所有实例字段。
    (此步骤可以使用private void readObject(ObjectInputStream)方法自定义)
  • 最后,readResolve()调用一个方法 - 如果存在这样的方法。其结果用作反序列化对象的结果。

(这不适用于records、java.lang.Classjava.lang.Stringjava.lang.Enum... 的实例)


在您的示例中,这意味着SerializableSingleton创建了您的新实例 - 绕过您的私有构造函数并调用java.lang.Object.<init>()- 因此您将看不到"Constructor is being called"输出。

现在您有两个“单例”实例。为了恢复原始的单例语义(该类只存在一个实例),我们用规范实例替换刚刚反序列化的实例:

private Object readResolve() {
    return getInstance();
}
Run Code Online (Sandbox Code Playgroud)

tl;dr:Java 的序列化很复杂,有时与黑魔法难以区分。使用 java 的序列化为一些令人惊讶的行为打开了大门。