Reputation: 1012
I am trying to write a Serializable Singleton class by adding the readResolve() method. My intention is to get the same object with its object's state at the time of serialization time.
below is my Test example code:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SingletonDemo {
public static void main(String[] args) {
Singleton obj = Singleton.getInstance();
System.out.println("After NEW Object creation : " + obj);
obj.i = 5;
System.out.println("Object modified");
System.out.println("After Object 1st Modification : " + obj);
serializeMe();
System.out.println("Serialized successfully with object state : " + obj);
obj.i = 10;
System.out.println("Object modified again");
System.out.println("After Object 2nd Modification : " + obj);
Singleton st = (Singleton)deSerializeMe();
System.out.println("Deserialized successfully");
System.out.println("After Deserialization : " + st);
}
public static void serializeMe() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("d:\\SingletonData.txt"));
oos.writeObject(MySerializedSingleton.getInstance());
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object deSerializeMe() {
ObjectInputStream oin = null;
Object obj = null;
try {
oin = new ObjectInputStream(new FileInputStream("d:\\SingletonData.txt"));
obj = oin.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
}
class Singleton implements Serializable {
int i;
private static Singleton obj = null;
private Singleton() {
System.out.println("Executing constructor");
i=1;
}
public static Singleton getInstance() {
if(obj == null) {
obj = new Singleton();
}
System.out.println("An instance is returned");
return obj;
}
/*private void writeObject(ObjectOutputStream oos) {
try {
oos.writeInt(i);
} catch (Exception e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream ois) {
try {
i = ois.readInt();
} catch (Exception e) {
e.printStackTrace();
}
}*/
public Object readResolve() {
System.out.println("Executing readResolve");
return Singleton.getInstance(); // FIXME
}
@Override
public String toString() {
return "Singleton [i=" + i + "]";
}
}
OUTPUT:
Executing constructor
An instance is returned
After NEW Object creation : Singleton [i=1]
Object modified
After Object 1st Modification : Singleton [i=5]
An instance is returned
Serialized successfully with object state : Singleton [i=5]
Object modified again
After Object 2nd Modification : Singleton [i=10]
Executing readResolve
An instance is returned
Deserialized successfully
After Deserialization : Singleton [i=10]
I know the current scenario will always return the same instance of Singleton with the latest Object's state.
I tried overriding the writeObject() and readObject() (commented in this above code) but not getting the desired result. i.e.
After Deserialization : Singleton [i=5]
But there is no reference of ObjectInputStream in readResolve() so that I can get the instance and update it with the serialized object's state before returning.
Please correct me if I am wrong in my conception and help me to solve this.
Thanks.
Upvotes: 9
Views: 39097
Reputation: 1
We can also do like that,call the getInstance()
method inside readResolve()
and store the result into some reference variable of type Singleton
then return the reference. Make sure that the return type of the the method should be of Object
type, you can provide any access modifier.
private Object readResolve() {
Singleton instance = getInstance();
return instance;
}
Upvotes: 0
Reputation: 1
I believe that simply returning this in the child class should do the trick
public Object readResolve() {
System.out.println("Executing readResolve");
return this;
}
I know it's a very old post but i stumbled upon it and other's might too.
Upvotes: 0
Reputation: 11443
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
}
}
Upvotes: 17
Reputation: 2019
This should do the trick (based on your initial question):
public Object readResolve() {
System.out.println("Executing readResolve");
if (obj == null) // optionally use external boolean flag to control this
{
System.out.println("readResolve - assigned obj = this - loaded state");
obj = this;
}
return Singleton.getInstance();
}
If you want to force loading the Singleton state then set obj = null
before deserializing the stored state.
Or you could add a boolean
flag that tells the readResolve()
method whether to keep or override obj
.
Be aware of multi-threading issues if you are working in a multi-threaded environment.
Upvotes: 0
Reputation: 1636
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.
Upvotes: 6
Reputation: 382
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.
Upvotes: 4
Reputation: 136042
try this
Object readResolve() {
Singleton s = getInstance();
s.i = i;
return s;
}
Note that readResolve dont need to be public.
Upvotes: 3