Creational

Singleton

Ensure a class has only one instance and provide a global point of access to it.

Beginner Pattern #01 of 23

What is the Singleton Pattern?

The Singleton pattern restricts the instantiation of a class to a single object and provides a global access point to that instance. It ensures that no matter how many times you ask for the object, you always get the same one.

It solves two problems at once: controlling how many instances of a class exist, and providing a single point of access. This is one of the simplest yet most debated patterns in software engineering.

Real-world analogy: A country can have only one official government at a time. No matter who asks "who is the government?", they all get the same answer — the single governing body.

The Problem It Solves

Imagine a logging service, a thread pool, or a configuration manager. If your application creates multiple instances of these, you'll have inconsistent state — two loggers writing to different files, two config objects with different values, two thread pools competing for resources.

You need a way to guarantee that only one instance ever exists, and that it is easily accessible throughout the codebase — without passing it around as a parameter everywhere.

How Singleton Solves It

The Singleton pattern has two components: make the default constructor private (so no one can call new on it), and provide a static creation method that acts as a constructor but returns the cached instance.

Singleton Class
Declares a private static field holding the single instance. Provides a public static getInstance() method. Has a private constructor to block external instantiation.

Visual Flow Diagram

Client A Client B Client C getInstance() static method null? new Instance() yes no static instance returned to all clients

Java Code Example

Java
// Thread-safe Singleton using double-checked locking
public class DatabaseConnection {

    // volatile ensures visibility across threads
    private static volatile DatabaseConnection instance;

    private String url;
    private String status;

    // Private constructor — no outside instantiation
    private DatabaseConnection() {
        this.url = "jdbc:postgresql://localhost/mydb";
        this.status = "connected";
        System.out.println("Connection established");
    }

    // Double-checked locking for performance + thread safety
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnection.class) {
                if (instance == null) {
                    instance = new DatabaseConnection();
                }
            }
        }
        return instance;
    }

    public String getUrl() { return url; }

    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

// --- Enum Singleton (best practice, serialization-safe) ---
public enum AppConfig {
    INSTANCE;

    private String env = "production";

    public String getEnv() { return env; }
    public void setEnv(String env) { this.env = env; }
}

// --- Client usage ---
public class Main {
    public static void main(String[] args) {
        DatabaseConnection db1 = DatabaseConnection.getInstance();
        DatabaseConnection db2 = DatabaseConnection.getInstance();

        System.out.println(db1 == db2); // true — same instance
        db1.query("SELECT * FROM users");

        AppConfig.INSTANCE.setEnv("staging");
        System.out.println(AppConfig.INSTANCE.getEnv()); // staging
    }
}

When to Use / Avoid

✓ Use When

  • Exactly one object is needed to coordinate actions (logging, config, thread pool)
  • Shared resource needs controlled access (DB connection, file system)
  • Global state is genuinely required
  • Lazy initialization of expensive objects is desired

✕ Avoid When

  • Unit testing is critical — Singletons are hard to mock/replace
  • You need multiple environments (dev/test/prod) with different instances
  • The "global" need is just convenience, not architectural necessity
  • Using in multi-classloader environments (each loader gets its own instance)

Real-World Examples

Pros & Cons

Pros

  • Guarantees single instance — prevents conflicting state
  • Lazy initialization — created only when needed
  • Global access point — no need to pass reference everywhere
  • Saves memory for expensive objects

Cons

  • Violates Single Responsibility Principle — manages both instance and business logic
  • Hard to unit test — global state leaks between tests
  • Multi-threading requires careful implementation
  • Hides dependencies — makes code harder to reason about

How Singleton Can Be Broken

⚠ Attack Vectors

  • Reflection: Constructor.setAccessible(true) bypasses the private constructor and creates a second instance
  • Serialization: Deserializing a saved Singleton creates a brand-new instance separate from the existing one
  • Cloning: If Singleton implements Cloneable, clone() produces a second instance
  • Multiple ClassLoaders: Each ClassLoader gets its own class definition — and its own Singleton instance
  • Multi-threading: Without synchronization, two threads can pass the null check simultaneously and both call new

✓ Prevention

  • Reflection fix: In the private constructor, throw an exception if the instance already exists: if (instance != null) throw new RuntimeException("Use getInstance()")
  • Serialization fix: Implement readResolve() to return the existing instance instead of the deserialized one
  • Cloning fix: Override clone() and throw CloneNotSupportedException
  • Best solution — Enum Singleton: Java enums are inherently serialization-safe, reflection-safe, and thread-safe. Use enum AppConfig { INSTANCE; } to eliminate all these vulnerabilities at once
  • Thread safety fix: Use volatile + double-checked locking, or initialize in a static initializer block
Java — Break & Fix
// ❌ BREAKING via Reflection
Constructor<DatabaseConnection> ctor =
    DatabaseConnection.class.getDeclaredConstructor();
ctor.setAccessible(true);
DatabaseConnection hack = ctor.newInstance(); // second instance!

// ✅ FIX — guard inside constructor
private DatabaseConnection() {
    if (instance != null)
        throw new RuntimeException("Use getInstance()");
}

// ❌ BREAKING via Serialization
// ObjectOutputStream writes the instance to disk
// ObjectInputStream.readObject() creates a NEW instance

// ✅ FIX — readResolve() returns the existing singleton
protected Object readResolve() { return getInstance(); }

// ✅ BEST FIX — Enum Singleton (immune to all attacks)
public enum SafeSingleton {
    INSTANCE;
    public void doWork() { /* ... */ }
}

How Other Patterns Relate

Interview Cheat Sheet

  1. What: Ensures only one instance of a class exists, with a global access point via static getInstance().
  2. How: Private constructor + private static field + public static method. Use volatile + double-checked locking for thread safety, or use Enum Singleton.
  3. When: Logging, configuration, connection pools, caches — shared resources that must be consistent. Avoid when testability matters — prefer Dependency Injection instead.