有效的Java项目#77 - 单例对象的序列化 - 为什么我必须使用readResolve?

时间:2011-02-22 13:32:26

标签: java serialization singleton

Effective Java#77声明我们必须在序列化期间使用readResolve来保留Singleton保证。他们使用了这个例子。

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

他们建议使用

  

如果是Elvis课程   实现Serializable,   以下readResolve方法就足够了   保证单身人士的财产:

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

此方法忽略   反序列化的对象,返回   杰出的埃尔维斯实例   在课堂上创建   初始化。

  • 现在的问题是 序列化再次加载类 有两个猫王的实例?
  • 如果类只加载一次 那么我们应该只有一个 自静电以来猫王的一个例子 字段不是序列化的 没有恢复期间 反序列化和
  • 其他猫王从哪里来 实例来自哪个 通过垃圾收集资金 readResolve(阻止来自 逃避deserializtaion 处理)。 这可以解释一下吗?

3 个答案:

答案 0 :(得分:4)

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

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

  • 反序列化构造一个对象而不调用执行任何可序列化类的构造函数(但它会执行最派生的非可序列化基类的no-arg构造函数)。

(顺便说一下,你没有创建你的类final,所以即使是一个子类也可以被反序列化。顺便说一下,你提出的readResolve方法不会被调用为子类,因为它是private。)

答案 1 :(得分:1)

我建议在你的单例类中创建一个代理类来保存你所有的变量。然后你可以有访问函数[getProxyClass()& setProxyClass()]。使代理类可序列化,然后当你去序列化或反序列化时使用代理类,只需使用访问器函数来获取或设置它。如果你这样做,它就会减少单身类所涉及的大量混乱。

答案 2 :(得分:0)

  
      
  • 现在问题是序列化是否再次加载类以拥有两个Elvis实例?
  •   

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

猫王模仿者(或很快变得)无法到达,并且被垃圾收集。

  
      
  • 如果只加载一次类,那么我们应该只有一个Elvis实例,因为静态字段没有被序列化,并且在反序列化期间没有恢复,
  •   

理想情况下是的。在实践中没有。

  
      
  • 其他Elvis实例从哪里获得的资格可以通过readResolve进行垃圾回收(防止转义反序列化过程)。这可以解释一下吗?
  •   

反序列化过程从创建第二个猫王(模仿者)开始,但readResolve方法确保没有任何东西可以看到它。

要了解这种情况的原因和原因,您需要了解readResolve()方法在反序列化中扮演的函数,如指定的here。基本上,当readResolve()方法返回INSTANCE时,它会说,“无论你在我们正在构建的图表中使用模仿者,都要使用真正的猫王”。