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 beprivateand 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)