Singleton Class in Java | Creational Design Pattern

Published on August 19, 2024

To implement a thread-safe Singleton using the Bill Pugh Singleton Design while also making it cloneable-proof, serializable-proof, and reflection-proof, you can combine several techniques into a single implementation. Here's how you can achieve this:

1. Bill Pugh Singleton Design (Static Inner Class): This ensures thread-safe and lazy initialization of the Singleton instance.

2. Prevent Cloning: Override the clone() method and throw a CloneNotSupportedException.

3. Prevent Serialization: Implement the readResolve() method to return the existing Singleton instance.

4. Prevent Reflection: Throw an exception in the constructor if an instance already exists.

Here's the complete implementation:

import java.io.Serializable;
 
public class Singleton implements Cloneable, Serializable {
    
    // Private constructor prevents instantiation from other classes
    private Singleton() {
        // Preventing instantiation via reflection
        if (SingletonHelper.INSTANCE != null) {
            throw new RuntimeException("Use getInstance() method to create");
        }
    }
 
    // Static inner class responsible for holding the Singleton instance
    private static class SingletonHelper {
        // The instance is created when the SingletonHelper class is loaded
        private static final Singleton INSTANCE = new Singleton();
    }
 
    // Global access point for the Singleton instance
    public static Singleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
 
    // Prevent cloning of the Singleton instance
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Cloning of this Singleton is not allowed");
    }
 
    // Ensure that during deserialization, the same instance is returned
    protected Object readResolve() {
        return getInstance();
    }
}

Key Components

  1. Static Inner Class (SingletonHelper)

    • This is the Bill Pugh approach. The INSTANCE is created only when the SingletonHelper class is loaded into memory. This ensures lazy initialization and thread safety without needing synchronization.
  2. Private Constructor

    • The private constructor prevents direct instantiation from other classes.
    • It also checks if the instance already exists (to prevent creation via reflection).
  3. Clone Prevention

    • The clone() method is overridden to throw a CloneNotSupportedException, preventing the Singleton instance from being cloned.
  4. Serialization Prevention

    • The readResolve() method ensures that during deserialization, the existing Singleton instance is returned instead of creating a new one.
  5. Reflection Prevention

    • The constructor checks if the instance already exists, and if so, it throws an exception to prevent creation via reflection.

How This Works

  • Thread Safety: The Bill Pugh approach ensures thread safety via the static inner class, which is loaded only when needed.
  • Cloning: Attempting to clone the Singleton instance will throw an exception, preserving the Singleton property.
  • Serialization: Deserializing the Singleton will not create a new instance; instead, it will return the existing instance.
  • Reflection: Even if someone tries to break the Singleton using reflection, the exception in the constructor will prevent multiple instances.

This implementation is robust and ensures that your Singleton class maintains a single instance across various potential pitfalls.