Effective Java Item #77 - Serialization of singlet

2020-02-06 04:56发布

Effective Java #77 states that we have to use readResolve to preserve the Singleton guarentee during serialization. They have used the example.

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

and they suggest using

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

// 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.

  • Now the question is does serialization loads the class again to have two instance of 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 eligble for garbage collection by readResolve (prevented from escaping the deserializtaion process). Can this be explained?

3条回答
劫难
2楼-- · 2020-02-06 05:25

I'd suggest creating a proxy class inside your singleton class to hold all of your variables. Then you can have accessor functions [getProxyClass() & setProxyClass()]. Make the proxy class serializable, then when you go to serialize or deserialize use the proxy class and just use the accessor functions to get or set it. If you do it this way it cuts out a lot of the mess involved with the singleton class.

查看更多
戒情不戒烟
3楼-- · 2020-02-06 05:32
  • Now the question is does serialization loads the class again to have two instance of Elvis?

A new instance of Elvis is created (the impersonator), but the overloaded readResolve method ensures that it is not returned as part of the data structure returned by ObjectInputStream.readObject().

The Elvis impersonator is (or soon becomes) unreachable, and is garbage collected.

  • 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

Ideally yes. In practice no.

  • 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?

The deserialization process starts out by creating the 2nd Elvis (the impersonator), but the readResolve method ensures that nothing ever sees it.

To understand how and why that is the case, you need to understand the function that a readResolve() method plays in deserialization, as specified here. Basically, when the readResolve() method returns INSTANCE it is saying, "wherever you were going to use the impersonator in the graph we are building, use the real Elvis instead".

查看更多
贪生不怕死
4楼-- · 2020-02-06 05:48
  • The class is only loaded once (unless you muck about with class loaders, but then the classes are actually different). Deserialisation of the above code does indeed create a new Elvis. You would need to use a serial proxy to avoid that.

  • Whilst there are two Elvis instances, Elvis.INSTANCE only points to one.

  • Deserialisation constructs an object without calling executing any constructors of serialisable classes (it does however execute the no-arg constructor of the most derived non-serialisable base class).

(And btw, you've not made your class final, so even a subclass can be deserialised. And, incidentally, your proposed readResolve method would not be called for a subclass as it is private.)

查看更多
登录 后发表回答