我的单例类如下:
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()
方法——如果存在这样的方法。它的结果被用作对象反序列化的结果。
(这不适用于record
s,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的writeExternal
和readExternal
方法接口都是由一个类来实现的,给出了类的完整对象流的格式和内容的控制其超。
所以writeReplace
和readResolve
方法给了你比普通的(和自动化的)序列化机制更多的控制,也就是说,在序列化之前/之后替换对象。它不是紧急链接到单例序列化,而是链接到,以实现序列化的代理模式. 但是正如你提到的,这种机制也用于实现Singleton的序列化。