关于Effective Java Item#77:Effective Java Item#77 – 单例对象的序列化 – 为什么我必须使用readResolve?

Effective Java Item #77 - Serialization of singleton objects - Why should I have to use readResolve?

有效的JavaSyl 77规定,在序列化过程中,我们必须使用EDCOX1×0来保存单独的守护程序。他们使用了这个例子。

1
2
3
4
public class Elvis implements Serializable{
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }

他们建议使用

If the Elvis class is made to
implement Serializable, the
following readResolve method suffices
to guarantee the singleton property:

1
2
3
4
5
// 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; }

This method ignores
the deserialized object, returning the
distinguished Elvis instance that was
created when the class was
initialized.

  • 现在的问题是序列化再次加载类有两个埃尔维斯的例子吗?
  • 如果类只加载一次那么我们应该只有一个ELVIS自静态以来的实例字段未序列化,并且期间未恢复反序列化和
  • 其他猫王从哪里来实例出现在垃圾收集清除readresolve(阻止正在转义反序列化过程)。这可以解释吗?

  • 类只被加载一次(除非你和类加载器混在一起,但是类实际上是不同的)。上述代码的反序列化确实创建了一个新的Elvis。您需要使用串行代理来避免这种情况。

  • 虽然有两个Elvis实例,但Elvis.INSTANCE只指向一个。

  • 反序列化在不调用任何可序列化类的构造函数的情况下构造一个对象(但它确实执行最派生的不可序列化基类的no arg构造函数)。

(顺便说一句,您还没有创建类final,因此甚至可以反序列化子类。顺便说一句,您所建议的readResolve方法不会被用于子类,因为它是private


我建议在singleton类中创建一个代理类来保存所有变量。然后您可以拥有访问函数[GetProxyClass()&;setProxyClass()]。使代理类可序列化,然后当您进行序列化或反序列化时,使用代理类并只使用访问器函数来获取或设置它。如果你这样做的话,它就可以减少单身阶级的混乱。


  • Now the question is does serialization loads the class again to have two instance of Elvis?

创建了elvis的新实例(模拟程序),但重载的readResolve方法确保它不会作为ObjectInputStream.readObject()返回的数据结构的一部分返回。

ELVIS模拟程序无法访问(或很快将无法访问),并被垃圾收集。

  • If the class is loaded only once then we should be having only one instance of Elvis since static fields are not serialized and are not restored during deserialization and

理想地是的。在实践中没有。

  • From where does the other Elvis instance comes which is made eligible for garbage collection by readResolve (prevented from escaping the deserialization process). Can this be explained?

反序列化过程从创建第二个ELVIS(模拟程序)开始,但readResolve方法确保没有任何人看到它。

要理解这种情况的方式和原因,您需要了解readResolve()方法在反序列化中扮演的函数,如本文所述。基本上,当readResolve()方法返回INSTANCE时,它会说,"无论您要在我们正在构建的图形中使用冒名器,请使用真正的elvis"。