Sandy
Sandy

Reputation: 1012

Implementation of readResolve() method for Serializable Singleton Instance

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

Answers (7)

Abhinav Katyayen
Abhinav Katyayen

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

Arijeet Chakravarty
Arijeet Chakravarty

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

Jk1
Jk1

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

Frederic Leitenberger
Frederic Leitenberger

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

Srini
Srini

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

Kaashan
Kaashan

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

Evgeniy Dorofeev
Evgeniy Dorofeev

Reputation: 136042

try this

   Object readResolve() {
      Singleton s = getInstance();
      s.i = i;
      return s;
   }

Note that readResolve dont need to be public.

Upvotes: 3

Related Questions