I'm always excited to take on new projects and collaborate with innovative minds.
say@niteshsynergy.com
https://www.niteshsynergy.com/
Welcome To Design Pattern Blog By Nitesh Synergy
A design pattern is a proven, reusable solution to a common problem in software design. It is not a complete code, but a template or guideline on how to solve specific design issues in object-oriented software.
Think of design patterns like blueprints—software architects use them to build robust, maintainable, and scalable applications.
Design patterns are broadly classified into 3 main categories:
Used when object creation is complex or needs to be controlled.
| Pattern Name | Purpose |
|---|---|
| Singleton | Only one instance of a class. |
| Factory Method | Create objects without exposing the exact class. |
| Abstract Factory | Factory of related factories. |
| Builder | Step-by-step object creation. |
| Prototype | Clone existing objects. |
Singleton is a creational design pattern that ensures:
Use Singleton when:
Thread Pool
Avoid Singleton when:
Rules For Singleton Pattern:
Ensure the class constructor is private to prevent instantiation from outside the class.
Create a static (ideally final) variable to hold the single instance of the class.
Provide a public static method or property that returns the same instance every time it's called.
In multithreaded environments, ensure that the instance creation logic is thread-safe to avoid multiple instances being created.
Override the cloning behavior to avoid duplication of the singleton object via clone().
Implement mechanisms (like readResolve() in Java) to prevent deserialization from creating a new instance.
Add checks inside the constructor to throw an exception if an instance already exists, thus protecting against reflection-based instantiation.
Use a static block to eagerly initialize the singleton and include logic to prevent reflection-based violations if needed.
final KeywordMark the instance variable as final to ensure immutability and prevent reassignment after initialization.
✅Ways To Achieve Singleton Pattern:
Sol-1:
Basic Singleton Implementation (Eager Initialization):
public class Singleton {
private static final Singleton INSTANCE = new Singleton(); // Static instance
private Singleton() {
// Private constructor
}
public static Singleton getInstance() {
return INSTANCE; // Global access point
}
}
public class Singleton {
private static Singleton instance;
private Singleton() {
// Private constructor
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}Sol-3:
Thread-Safe Singleton (Synchronized Method)
public class Singleton {
private static Singleton instance;
private Singleton() {
// Private constructor
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}Sol-4:
Thread-Safe Singleton (Double-Checked Locking)
→ For all keywords -https://www.niteshsynergy.com/java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// Private constructor
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}public class Singleton {
private static Singleton instance;
private Singleton() {
// Prevent Reflection from creating another instance
if (instance != null) {
throw new IllegalStateException("Singleton instance already created!");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}Sol-6:
Singleton with Enum (Best Practice):
// Singleton Enum for Logging Utility
public enum Logger {
INSTANCE;
// Method to log messages
public void log(String message) {
System.out.println("LOG: " + message);
}
// Method to log errors
public void logError(String errorMessage) {
System.err.println("ERROR: " + errorMessage);
}
}
public class UserService {
// Some user-related logic
public void createUser(String username) {
// Log an informational message
Logger.INSTANCE.log("User creation started for: " + username);
try {
// Simulating user creation logic
if (username == null || username.isEmpty()) {
throw new IllegalArgumentException("Username cannot be null or empty");
}
// Simulate user created
Logger.INSTANCE.log("User created successfully: " + username);
} catch (Exception e) {
// Log the error using Singleton Logger
Logger.INSTANCE.logError("Error creating user: " + e.getMessage());
}
}
}Sol-7:
Preventing Cloning via the clone() Method:
public class Singleton {
private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new IllegalStateException("Singleton instance already created!");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
@Override
public Object clone() {
throw new CloneNotSupportedException("Cloning of Singleton is not allowed!");
}
}
Sol-8:
Preventing Serialization Attack:
public class Singleton implements Serializable {
private static final long serialVersionUID = 1L;
private static Singleton instance;
private Singleton() {
if (instance != null) {
throw new IllegalStateException("Singleton instance already created!");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// Ensure Singleton instance is preserved during serialization/deserialization
protected Object readResolve() {
return instance;
}
}
Sol-9:
Using a Static Block (Eager Initialization with Reflection Safety):
public class Singleton {
private static final Singleton instance;
static {
try {
instance = new Singleton();
} catch (Exception e) {
throw new IllegalStateException("Singleton instance already created!");
}
}
private Singleton() {
// Prevent Reflection from creating another instance
if (instance != null) {
throw new IllegalStateException("Singleton instance already created!");
}
}
public static Singleton getInstance() {
return instance;
}
}
Sol-10:
Use of final Keyword:
public class Singleton {
private static final Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {
// Prevent Reflection from creating another instance
if (instance != null) {
throw new IllegalStateException("Singleton instance already created!");
}
}
public static Singleton getInstance() {
return instance;
}
}→There are many other option to create Singleton class.
Below code for Singleton breaking ways:
//Code to break: 1. Reflection
class BreakWithReflection {
public static void breakWithReflection() {
try {
Singleton instance1 = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true); // Bypass private access
Singleton instance2 = constructor.newInstance();
System.out.println(" From breakWithReflection");
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
//2. Serialization/Deserialization way break
class BreakWithSerialization {
public static void breakWithSerialization() throws Exception {
Singleton instance1 = Singleton.getInstance();
// Serialize instance
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(instance1);
out.close();
// Deserialize instance
ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println(" From breakWithSerialization");
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
// 3. Cloning way break
class BreakWithCloning {
public static void breakWithCloning() throws CloneNotSupportedException {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = (Singleton) instance1.clone(); // If clone() is accessible
System.out.println(" From breakWithCloning");
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
// 4. Multithreading (Race Condition)
class BreakWithMultithreading {
public static void breakWithMultithreading() {
Runnable task = () -> {
Singleton instance = Singleton.getInstance();
System.out.println(instance.hashCode());
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
System.out.println(" From BreakWithMultithreading");
thread1.start();
thread2.start();
}
}
//5. Garbage Collection
class BreakWithGarbageCollection {
public static void breakWithGarbageCollection() {
Singleton instance = Singleton.getInstance();
WeakReference<Singleton> weakRef = new WeakReference<>(instance);
instance = null; // Nullify the strong reference
System.gc(); // Force garbage collection
System.out.println(" From breakWithGarbageCollection");
Singleton newInstance = Singleton.getInstance();
System.out.println(weakRef.get() == newInstance); // False if GC removed the weak reference
}
}
//6. Subclassing
class BreakWithSubclassing {
public static void breakWithSubclassing() {
// Singleton subclassInstance = new Singleton() {}; // Anonymous subclass
// System.out.println(subclassInstance.hashCode());
}
}
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// // If clone() is accessible, // 3. Cloning way break
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void showMessage() {
System.out.println("Singleton Instance");
}
}package com.niteshsynergy.designPattern.CreationalDesign;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
public class Demo1 {
public static void main(String[] args) throws Exception {
/* Singleton s1 = Singleton.getInstance();
System.out.println(s1.hashCode());
Singleton s2 = Singleton.getInstance();
System.out.println(s2.hashCode());
*/
BreakWithReflection.breakWithReflection();
BreakWithSerialization.breakWithSerialization();
BreakWithCloning.breakWithCloning();
BreakWithMultithreading.breakWithMultithreading();
BreakWithGarbageCollection.breakWithGarbageCollection();
BreakWithSubclassing.breakWithSubclassing();
}
//Prevention Strategies:
//Prevent Reflection: Add a check in the private constructor.
/* private Singleton() {
if (instance != null) {
throw new IllegalStateException("Instance already created!");
}
}*/
//Serialization Protection: Implement readResolve().
/* protected Object readResolve() {
return instance;
}*/
//Prevent Cloning: Override clone() and throw an exception.
//java
//Copy code
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Cannot clone singleton");
}
//Thread-Safe Initialization: Use Bill Pugh Singleton or Double-Checked Locking.
//Final Class: Declare the class as final to prevent subclassing.
}✅ Real-World Use Cases of Singleton Class
Use Case: Centralized Transaction Manager
🔹 Why Singleton?
Use Case: DatabaseConnectionManager Singleton
🔹 Why Singleton?
Use Case: GameSettings or GameEngine Singleton
🔹 Why Singleton?
Use Case: AIModelManager or APIClientManager
Imagine building an AI-based tool like ChatGPT:
🔹 Why Singleton?
| Domain | Singleton Class Example | Purpose |
|---|---|---|
| Banking | TransactionManager | Maintain consistency and log transactions |
| JDBC/Backend | DatabaseConnectionManager | Reuse DB connections, optimize resource usage |
| Gaming | GameEngine / GameSettings | Global settings and logic across game screens |
| AI Tools | AIModelManager / APIClient | Centralized model config and API management |
GameEngineSingleton.java — Perfect Singleton for Game Engine