stock market

How to Implement Singleton Design Pattern in Java (Step-by-Step)

Spread the love
What is singleton design pattern

A Singleton Pattern says that just “define a class that has only one instance and provides a global point of access to it . If singleton class is loaded by two classloaders, two instance of singleton class will be created, one for each classloaders.

Key Concepts

  • Only one object is created
  • Global access to that object
  • Object is controlled by the class itself
1. Significance of Serialization in Singleton Pattern

Serialization in Java means converting an object into a byte stream so it can be:

  • Saved to a file
  • Sent over a network
  • Stored in a database

Deserialization is the reverse process (byte stream → object). Even if your class ensures only one instance, deserialization creates a NEW object, which violates the Singleton rule.

Example: Singleton Class (Without Protection)

  1. Singleton.java
import java.io.Serializable;
class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

2.TestSingleton.java

import java.io.*;

public class TestSingleton {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();

        // Serialize
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream("rkdigital.ser"));
        out.writeObject(instance1);
        out.close();

        // Deserialize
        ObjectInput in = new ObjectInputStream(new FileInputStream("rkdigital.ser"));
        Singleton instance2 = (Singleton) in.readObject();
        in.close();

        // Compare instances
        System.out.println(instance1 == instance2); 
    }
}

output

false // false that means Singleton is broken

Why This Happens?

During deserialization:

  • JVM does NOT use your constructor
  • It creates a new object from byte stream

To resolve this issue, you need to override the readResolve() method that enforces the singleton. It is called just after the object is deserialized. It returns the singleton object.

  1. Singleton.java
import java.io.Serializable;

class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }

    // Prevents new object creation during deserialization
    protected Object readResolve() {
        return instance;
    }
}

2. TestSingleton.java

import java.io.*;

public class TestSingleton {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();

        // Serialize
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream("rkdigital.ser"));
        out.writeObject(instance1);
        out.close();

        // Deserialize
        ObjectInput in = new ObjectInputStream(new FileInputStream("rkdigital.ser"));
        Singleton instance2 = (Singleton) in.readObject();
        in.close();

        // Compare instances
        System.out.println(instance1 == instance2); 
    }
}

Output :

ture

2.Inheritance of Singleton Class

Inheriting a singleton class should be prohibited. Making a singleton class inheritable means any number of child classes can inherit from it creating multiple instances of the singleton class which will obviously violate the principle of singletons.

If you allow inheritance:

  • Each subclass can create its own instance
  • This breaks the “single instance” rule

Example : Singleton class with Inheritance

  1. Singleton.java
class Singleton {

    protected static Singleton instance;

    protected Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

2. ChildSingleton.java

class ChildSingleton extends Singleton {

    private static ChildSingleton childInstance;

    private ChildSingleton() {}

    public static ChildSingleton getInstance() {
        if (childInstance == null) {
            childInstance = new ChildSingleton();
        }
        return childInstance;
    }
}

3. SingletonTest.java

public class SingletonTest {
    public static void main(String[] args) {
        Singleton parent = Singleton.getInstance();
        ChildSingleton child = ChildSingleton.getInstance();

        System.out.println(parent == child);
    }
}

output :

false // false means Singleton is broken across hierarchy

To address the above issues, please follow the steps outlined below.

  • Make class final
  • Private constructor
3. Cloning in singleton

Cloning is a concept to create duplicate objects. Using clone we can create copy of object. Suppose, we create clone of a singleton object, then it will create a copy that is there are two instances of a singleton class, hence the class is no more singleton.

  1. Singleton.java
class Singleton implements Cloneable {

    private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // ❌ creates new object
    }
}

2. Test.java

public class Test {
    public static void main(String[] args) throws Exception {

        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = (Singleton) obj1.clone();

        System.out.println(obj1 == obj2); // false 
    }
}
  • clone() creates a new instance in memory
  • It bypasses the Singleton control logic

To overcome this issue, override clone() method and throw an exception from clone method that is CloneNotSupportedException. Now whenever user will try to create clone of singleton object, it will throw exception and hence our class remains singleton .

class Singleton implements Cloneable {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException(“Cloning not allowed in Singleton”);
}
}

4.Reflection in Singleton Pattern

Inspecting and modifying classes, constructors, methods, and fields at runtime, even if they are private. Reflection can call a private constructor and create a new object, This breaks the Singleton guarantee.

  1. Singleton.java
class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

2. Test.java

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance()

        // Using reflection to break Singleton
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true); // bypass private
        Singleton instance2 = constructor.newInstance();

        System.out.println(instance1 == instance2); 
    }
}

output :

false //Singleton is broken

  • Reflection bypasses private constructor
  • JVM allows forced object creation

To overcome the above issues , add protection in constructor.

class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
        if (instance != null) {
            throw new RuntimeException("Use getInstance() method");
        }
    }
    public static Singleton getInstance() {
        return instance;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *