有效的Java项目#77 – 单例对象的序列化 – 为什么我必须使用readResolve?
有效的Java #77声明我们必须使用readResolve
在序列化期间保留Singleton保证。 他们使用了这个例子。
public class Elvis implements Serializable{ public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... }
他们建议使用
如果Elvis类用于实现Serializable,则以下readResolve方法足以保证singleton属性:
// readResolve for instance control - you can do better! private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
此方法忽略反序列化对象,返回在初始化类时创建的区分Elvis实例。
- 现在问题是序列化再次加载类有两个Elvis实例吗?
- 如果只加载一次类,那么我们应该只有一个Elvis实例,因为静态字段没有被序列化,并且在反序列化期间不会被恢复。
- 从另一个Elvis实例到哪里可以通过readResolve进行垃圾收集(防止逃避deserializtaion进程)。 这可以解释一下吗?
-
该类只加载一次(除非您使用类加载器,但实际上类不同)。 上述代码的反序列化确实创建了一个新的
Elvis
。 您需要使用串行代理来避免这种情况。 -
虽然有两个
Elvis
实例,但Elvis.INSTANCE
只指向一个。 -
反序列化构造一个对象而不调用执行可序列化类的任何构造函数(但它会执行最派生的非可序列化基类的无参数构造函数)。
(顺便说一句,你没有让你的类final
,所以即使是一个子类也可以被反序列化。顺便说一句,你提出的readResolve
方法不会被调用为子类,因为它是private
。)
我建议在你的单例类中创建一个代理类来保存你所有的变量。 然后你可以有访问函数[getProxyClass()&setProxyClass()]。 使代理类可序列化,然后当你去序列化或反序列化时使用代理类,只需使用访问器函数来获取或设置它。 如果你这样做,它会减少与单身人士课程相关的大量混乱。
- 现在问题是序列化再次加载类有两个Elvis实例吗?
创建了一个新的Elvis实例(模仿者),但重载的readResolve
方法确保它不会作为ObjectInputStream.readObject()
返回的数据结构的一部分返回。
猫王模仿者(或很快变得)无法到达,并且被垃圾收集。
- 如果只加载一次类,那么我们应该只有一个Elvis实例,因为静态字段没有被序列化,并且在反序列化期间不会被恢复。
理想的是。 在实践中没有。
- 从哪里获得其他Elvis实例,使其有资格通过readResolve进行垃圾收集(防止转义反序列化过程)。 这可以解释一下吗?
反序列化过程从创建第二个Elvis(模仿者)开始,但readResolve
方法确保没有任何东西可以看到它。
要了解这种情况的原因和原因,您需要了解readResolve()
方法在反序列化中所起的作用,如此readResolve()
指定。 基本上,当readResolve()
方法返回INSTANCE
它会说,“无论你在我们正在构建的图形中使用模仿者,都要使用真正的Elvis”。