DEV Community

Thellu
Thellu

Posted on

How to Prevent Serialization from Breaking the Singleton Pattern in Java

Singleton is a commonly used design pattern that ensures a class has only one instance and provides a global point of access to it.

However, serialization can silently break the Singleton guarantee by creating a new instance during deserialization. In this article, we'll explore why that happens and how to fix it.


The Problem: Serialization Breaks Singleton

Let's start with a basic Singleton class:

import java.io.*;

class Singleton implements Serializable {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now let's serialize and deserialize the singleton:

Singleton instance1 = Singleton.getInstance();

// Serialize to a file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(instance1);
out.close();

// Deserialize from the file
ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton instance2 = (Singleton) in.readObject();
in.close();

System.out.println(instance1 == instance2);  // false ❌
Enter fullscreen mode Exit fullscreen mode

Even though the Singleton was correctly implemented, deserialization created a new object, violating the Singleton property.


The Fix: Implement readResolve()

Java provides a hook called readResolve() that lets you control what object is returned during deserialization.

Fix the Singleton class like this:

class Singleton implements Serializable {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }

    // This ensures that deserialization returns the original instance
    private Object readResolve() throws ObjectStreamException {
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now serialization and deserialization will preserve the singleton instance:

System.out.println(instance1 == instance2);  // true ✅
Enter fullscreen mode Exit fullscreen mode

Notes

  • The readResolve() method must be private and return Object.
  • Without it, Java's serialization mechanism creates a new instance.
  • Always test singleton classes that implement Serializable.

Bonus: Prevent Reflection-Based Attacks (Optional)

Serialization isn't the only threat to Singleton. Reflection can also break it.

You can throw an exception in the constructor if an instance already exists:

private Singleton() {
    if (instance != null) {
        throw new RuntimeException("Use getInstance() method to get the single instance of this class");
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Serialization can undermine Singleton, but readResolve() is a simple yet powerful tool to maintain the Singleton contract.

Always be cautious when marking a Singleton class as Serializable, especially in distributed systems, caching, or when passing objects over the network.

Top comments (0)