8

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.

Sushant Somani
  • 1,450
  • 3
  • 13
  • 31
Sandy
  • 972
  • 5
  • 15
  • 28

7 Answers7

17

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
}

}

Jk1
  • 11,233
  • 9
  • 54
  • 64
  • Thanks Jk1. But one doubt that I have is what is the sequence of executing readObject() and readResolve() at the time of deserialization? – Sandy Jun 30 '13 at 08:15
  • One minor caveat, I would throw an `assertionError()` inside the private constructor just to guard against reflection attacks or someone accidentally instantiating `Singleton` from within the class. – Srini May 27 '15 at 20:24
  • 3
    The above code violates the Singleton property, i.e. if you print at the end both `s` and `s.getInstance()` you see that two instances exist. The fault is the redefinition of `instace` in method `readObject`. Actually, overriding `readObject` is not necessary. The solution below given by @Dorofeev is a correct one. – Dominik Jan 26 '19 at 18:27
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.

Community
  • 1
  • 1
Srini
  • 1,626
  • 2
  • 15
  • 25
  • Bad answer, as data stored in an enum is *not* serialized! The original use case to revert to an old version is *not* possible using enums. – Dominik Jan 26 '19 at 18:34
  • @Dominik that is incorrect. https://www.baeldung.com/jackson-serialize-enums – Zahid Khan Jun 14 '22 at 06:41
  • @ZahidKhan, you refer to serialization using Jackson, but the this thread deals with the built-in Java Serialization. According to the Spec, field values are not persisted (see https://docs.oracle.com/en/java/javase/17/docs/specs/serialization/serial-arch.html#serialization-of-enum-constants) – Dominik Jun 16 '22 at 11:06
4

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.

Kaashan
  • 352
  • 3
  • 12
3

try this

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

Note that readResolve dont need to be public.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
0

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.

Frederic Leitenberger
  • 1,949
  • 24
  • 32
0

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.

0

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;
}
Mickael Maison
  • 25,067
  • 7
  • 71
  • 68