Sitemap
Java Codeex

Java Codeex

Singleton Design Pattern in Java

7 min readJul 8, 2026

--

Indetail about Singleton Design Pattern

Press enter or click to view image in full size

1. Introduction

The Singleton Design Pattern is a creational design pattern that ensures only one object of a class is created throughout the application.

It provides a global access point to that single object.

Singleton is commonly used when we need only one shared instance of a class, such as:

  • Logger
  • Database connection manager
  • Configuration manager
  • Cache manager
  • Thread pool manager

2. Purpose of Singleton Pattern

The main purpose of Singleton is to control object creation.

Instead of allowing multiple objects to be created using new, Singleton restricts object creation and provides the same instance whenever required.

3. Real-Time Example

Assume we have an application-level configuration class.

The configuration data should be loaded only once and reused across the application.

public class AppConfig {
private static AppConfig instance;
private AppConfig() {
System.out.println("AppConfig object created");
}
public static AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
}

Usage:

public class Main {
public static void main(String[] args) {
AppConfig config1 = AppConfig.getInstance();
AppConfig config2 = AppConfig.getInstance();
System.out.println(config1 == config2);
}
}

Output:

AppConfig object created
true

Both config1 and config2 point to the same object.

4. Key Steps to Create Singleton

To create a Singleton class, we usually follow these steps:

  1. Create a private static instance variable.
  2. Make the constructor private.
  3. Provide a public static method to return the instance.

Example:

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

5. Types of Singleton Implementation

5.1 Eager Initialization

In eager initialization, the object is created at the time of class loading.

public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}

Advantages

  • Simple implementation
  • Thread-safe by default
  • No synchronization required

Disadvantages

  • Object is created even if it is not used
  • Not suitable if object creation is heavy

5.2 Lazy Initialization

In lazy initialization, the object is created only when it is requested for the first time.

public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

Advantages

  • Object is created only when needed
  • Saves memory if the object is never used

Disadvantages

  • Not thread-safe
  • Multiple threads can create multiple objects at the same time

5.3 Thread-Safe Singleton Using Synchronized Method

public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}

Advantages

  • Thread-safe

Disadvantages

  • Performance issue because the whole method is synchronized
  • Synchronization happens every time getInstance() is called

5.4 Double-Checked Locking Singleton

Double-checked locking improves performance by reducing synchronization overhead.

public class DoubleCheckedSingleton {
private static volatile DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {
}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}

Why volatile is used?

The volatile keyword ensures that changes made by one thread are visible to other threads.

Without volatile, due to instruction reordering, another thread may see a partially created object.

Advantages

  • Thread-safe
  • Better performance than synchronized method
  • Object is created lazily

Disadvantages

  • Slightly complex implementation

5.5 Bill Pugh Singleton Design

This is one of the best approaches before using Enum Singleton.

public class BillPughSingleton {
private BillPughSingleton() {
}
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}

Advantages

  • Lazy initialization
  • Thread-safe
  • No synchronization required
  • Simple and efficient

The static inner class is loaded only when getInstance() is called.

6. How Singleton Breaks

Singleton can be broken in different ways:

  1. Reflection
  2. Serialization and deserialization
  3. Cloning
  4. Multithreading, if not implemented properly

This document focuses mainly on Reflection and Enum Singleton.

7. How Singleton Breaks with Reflection

Even if the constructor is private, Java Reflection can access the private constructor and create another object.

Get Java Codeex’s stories in your inbox

Join Medium for free to get updates from this writer.

Example:

import java.lang.reflect.Constructor;
public class SingletonReflectionBreakExample {
public static void main(String[] args) throws Exception {
Singleton instance1 = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance2 = constructor.newInstance();
System.out.println(instance1 == instance2);
}
}

Singleton class:

public class Singleton {

private static Singleton instance;
private Singleton() {
System.out.println("Singleton constructor called");
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

Output:

Singleton constructor called
Singleton constructor called
false

Here, two objects are created:

  • One object from getInstance()
  • Another object using Reflection

So, Reflection breaks the Singleton principle.

8. Prevent Singleton Breaking Using Constructor Guard

We can add a check inside the constructor.

public class Singleton {

private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new RuntimeException("Singleton object already created. Reflection is not allowed.");
}
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

Now, if Reflection tries to create another object after the first object is already created, it throws an exception.

However, this approach is not fully safe in all cases.

For example, if Reflection creates the object before getInstance() is called, the protection may not work as expected.

9. Best Solution: Enum Singleton

Enum Singleton is the best and safest way to implement Singleton in Java.

public enum EnumSingleton {
INSTANCE;
public void showMessage() {
System.out.println("Enum Singleton method called");
}
}

Usage:

public class Main {
public static void main(String[] args) {
EnumSingleton singleton1 = EnumSingleton.INSTANCE;
EnumSingleton singleton2 = EnumSingleton.INSTANCE;
singleton1.showMessage();
System.out.println(singleton1 == singleton2);
}
}

Output:

Enum Singleton method called
true

10. Why Enum Singleton is Best

Enum Singleton protects against:

10.1 Reflection

Java does not allow creating enum objects using Reflection.

If we try to create an enum object using Reflection, Java throws an exception.

10.2 Serialization and Deserialization

Normal Singleton can break during serialization and deserialization.

But Enum Singleton is serialization-safe by default.

10.3 Thread Safety

Enum instances are created by JVM in a thread-safe manner.

So no extra synchronization is required.

10.4 Simplicity

Enum Singleton requires very less code.

public enum Singleton {
INSTANCE;
}

11. Reflection Attack on Enum Singleton

Example:

import java.lang.reflect.Constructor;
public class EnumReflectionTest {
public static void main(String[] args) throws Exception {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
Constructor<EnumSingleton> constructor =
EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumSingleton instance2 = constructor.newInstance("INSTANCE2", 1);
System.out.println(instance1 == instance2);
}
}

Output:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

This proves that Enum Singleton is protected from Reflection.

12. Singleton Breaking Using Serialization

Normal Singleton can also break during serialization and deserialization.

Example:

import java.io.Serializable;
public class SerializableSingleton implements Serializable {
private static SerializableSingleton instance = new SerializableSingleton();
private SerializableSingleton() {
}
public static SerializableSingleton getInstance() {
return instance;
}
}

Test:

import java.io.*;
public class SerializationBreakExample {
public static void main(String[] args) throws Exception {
SerializableSingleton instance1 = SerializableSingleton.getInstance();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(instance1);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SerializableSingleton instance2 = (SerializableSingleton) in.readObject();
in.close();
System.out.println(instance1 == instance2);
}
}

Output:

false

Deserialization creates a new object.

13. Prevent Serialization Breaking Using readResolve()

import java.io.Serializable;
public class SerializableSingleton implements Serializable {
private static final SerializableSingleton INSTANCE = new SerializableSingleton();
private SerializableSingleton() {
}
public static SerializableSingleton getInstance() {
return INSTANCE;
}
protected Object readResolve() {
return INSTANCE;
}
}

Now deserialization returns the existing instance instead of creating a new one.

14. Singleton Breaking Using Cloning

If Singleton class implements Cloneable, cloning can create another object.

public class CloneSingleton implements Cloneable {
private static final CloneSingleton INSTANCE = new CloneSingleton();
private CloneSingleton() {
}
public static CloneSingleton getInstance() {
return INSTANCE;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

This can break Singleton.

Prevention

Override clone() and throw exception.

@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Cloning is not allowed for Singleton");
}

15. Final Recommended Singleton Approaches

Best General Approach

Use Enum Singleton.

public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Singleton logic");
}
}

Best Non-Enum Approach

Use Bill Pugh Singleton.

public class Singleton {

private Singleton() {
}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}

16. Interview Explanation

Singleton Design Pattern is a creational design pattern that allows only one instance of a class and provides a global access point to it. We usually create Singleton by making the constructor private, keeping a static instance variable, and exposing a public static getInstance() method.

Singleton can be implemented using eager initialization, lazy initialization, synchronized method, double-checked locking, Bill Pugh approach, and Enum. Singleton can be broken using Reflection because Reflection can access a private constructor and create another object. It can also be broken by serialization and cloning.

The best way to implement Singleton in Java is using Enum because it is thread-safe, serialization-safe, and protected from Reflection by default.

17. Comparison Table

Press enter or click to view image in full size

18. Key Points to Remember

  • Singleton restricts object creation to one instance.
  • Constructor should be private.
  • Static method provides global access.
  • Lazy Singleton is not thread-safe.
  • Double-checked locking needs volatile.
  • Reflection can break private constructors.
  • Serialization can break Singleton unless readResolve() is used.
  • Cloning can break Singleton unless clone() is restricted.
  • Enum Singleton is the safest and simplest approach.

--

--