Singleton

One Instance Only

Singleton ensures a class has exactly one instance and gives global access to it — for config, a connection pool or a cache. Three common implementations:

Example

public class Main {
    public static void main(String[] args) {
        System.out.println(AppConfig.INSTANCE.get("currency"));
        System.out.println("Same enum instance: " + (AppConfig.INSTANCE == AppConfig.valueOf("INSTANCE")));

        System.out.println("Same holder instance: " + (IdGenerator.getInstance() == IdGenerator.getInstance()));
        System.out.println(IdGenerator.getInstance().next() + ", " + IdGenerator.getInstance().next());

        System.out.println("Same DCL instance: " + (Registry.getInstance() == Registry.getInstance()));
    }
}

// 1. Enum singleton: simplest, thread-safe, safe against reflection and serialization (Effective Java's choice)
enum AppConfig {
    INSTANCE;
    private final java.util.Map<String, String> values = java.util.Map.of("currency", "INR");
    String get(String key) { return values.get(key); }
}

// 2. Initialization-on-demand holder: lazy and thread-safe thanks to class loading rules
class IdGenerator {
    private int counter = 1000;
    private IdGenerator() {}
    private static class Holder { static final IdGenerator INSTANCE = new IdGenerator(); }
    static IdGenerator getInstance() { return Holder.INSTANCE; }
    synchronized int next() { return ++counter; }
}

// 3. Double-checked locking: needs volatile
class Registry {
    private static volatile Registry instance;
    private Registry() {}
    static Registry getInstance() {
        if (instance == null) {
            synchronized (Registry.class) {
                if (instance == null) instance = new Registry();
            }
        }
        return instance;
    }
}

Output

INR
Same enum instance: true
Same holder instance: true
1001, 1002
Same DCL instance: true

In Practice

Note: Singletons are global state and make testing harder. In Spring you rarely write one: every @Service/@Component bean is a singleton by default, managed by the container and injected where needed.