有效的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”。