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; } }
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 ❌
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; } }
Now serialization and deserialization will preserve the singleton instance:
System.out.println(instance1 == instance2); // true ✅
Notes
- The
readResolve()
method must beprivate
and returnObject
. - 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"); } }
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)