单例类序列化



我的单例类如下:

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;
}
}

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

但我的问题是——这怎么可能?当一个静态类成员不能被序列化时,反序列化的问题是怎么来的呢?网上到处都是?

由于单例对象是静态的:

private static SerializableSingleton instance = new SerializableSingleton();

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

序列化绕过了语言中的很多东西,甚至做了一些普通反射无法做到的事情。

当一个对象被序列化时,它的类名和所有实例字段(非transient)被写入流。

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

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

(这不适用于records,java.lang.Class,java.lang.String,java.lang.Enum…的实例)


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

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

private Object readResolve() {
return getInstance();
}

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

正如文档所说:

通过实现readResolve方法,类可以直接控制被反序列化的自身实例的类型和实例. 的方法定义如下:

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

:

…例如,可以创建一个Symbol类,其中只有一个每个符号绑定的单个实例都存在于一个虚函数中机器。将实现readResolve方法来确定是否那个符号已经定义好了,可以代替先前存在的符号等效Symbol对象来维护标识约束. 在这个符号对象的惟一性可以被保持序列化。

:

  • 一个writeObject方法来控制什么信息被保存…
  • 一个readObject方法[..]读取写入的信息…
  • 一个writeReplace方法,允许一个类指定一个要写入流的替换对象
  • 一个readResolve方法,允许类为刚从流中读取的对象指定一个替换对象

要完全控制序列化(也要序列化静态和瞬态字段),您必须实现Externalization接口:

Externalizable. c的writeExternalreadExternal方法接口都是由一个类来实现的,给出了类的完整对象流的格式和内容的控制其超。

所以writeReplacereadResolve方法给了你比普通的(和自动化的)序列化机制更多的控制,也就是说,在序列化之前/之后替换对象。它不是紧急链接到单例序列化,而是链接到,以实现序列化的代理模式. 但是正如你提到的,这种机制也用于实现Singleton的序列化。

最新更新