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.

Warp.dev image

Warp is the #1 coding agent.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

Top comments (0)

MongoDB Atlas runs apps anywhere. Try it now.

MongoDB Atlas runs apps anywhere. Try it now.

MongoDB Atlas lets you build and run modern apps anywhere—across AWS, Azure, and Google Cloud. With availability in 115+ regions, deploy near users, meet compliance, and scale confidently worldwide.

Start Free