Implementation of readResolve() method for Serializable Singleton Instance
Here's how it can be achived:
public class Singleton implements Serializable {
private static Singleton instance = new Singleton();
private int i;
public static Singleton getInstance() {
return instance;
}
private Singleton() {
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
instance = this;
}
private Object readResolve() {
return instance;
}
public static void main(String[] args) throws Throwable {
Singleton s = Singleton.getInstance();
s.i = 5;
ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(getInstance());
oos.close();
s.i = 7; //modified after serialization
InputStream is = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
Singleton deserialized = (Singleton) ois.readObject();
System.out.println(deserialized.i); // prints 5
}
}
The best way to implement a Serializable
Singleton is to use an Enum.
From Joshua Bloch's Effective Java:
"This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton."
Save yourself some time and use an Enum.
Refer this question for more discussion on the same topic.
The solution voted as correct though helps in retrieving the value of 'i' on the de-serialized object, it violates the singleton design pattern. After de-serialization, two objects of 'Singleton' class are created.
Proof: modify the main() method as below:
public static void main(String[] args) throws Throwable {
Singleton s = Singleton.getInstance();
s.i = 5;
System.out.println("before serialization::"+s.i+" "+ s); //printing value and object
ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(getInstance());
oos.close();
s.i = 7; //modified after serialization
System.out.println("modified after serialization::"+s.i+" "+s);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
Singleton deserialized = (Singleton) ois.readObject();
System.out.println("after deserialization::"+deserialized.i+" "+deserialized); //prints 5, but hashCode is different, which means object is not the same
}
Output is:
before serialization::5 serialization.Singleton@1690726
modified after serialization::7 serialization.Singleton@1690726
after deserialization::5 serialization.Singleton@1662dc8
Even the second suggestion has the same problem. I tried few more configurations, but nothing worked out. Is there any other way to solve this problem?
Please tag the thread with 'Singleton' as well so that it reaches wider audience.
Thanks.