Singleton Design Pattern in Java
Indetail about Singleton Design Pattern
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
trueBoth config1 and config2 point to the same object.
4. Key Steps to Create Singleton
To create a Singleton class, we usually follow these steps:
- Create a private static instance variable.
- Make the constructor private.
- 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:
- Reflection
- Serialization and deserialization
- Cloning
- 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
falseHere, 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
true10. 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 objectsThis 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:
falseDeserialization 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
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.

![Spring Boot 3 + Spring Security 6 With JWT Authentication and Authorization [New 2024]](https://webproxy.poorya-velaei-d67.workers.dev/https://miro.medium.com/v2/resize:fit:1358/format:webp/1*vsCw_bg8325pbPjZ88UMGg.png)







