🪆 Enums, Records & Nested Types · Intermediate

Enum singletons in Java

Why a single-element enum is the safest singleton.

🧩 The mysteryYour app must have exactly ONE configuration object. You write a private constructor — and a clever hacker still builds a second copy. How?

One and only

A singleton is a class with exactly one instance. The classic recipe is a private constructor plus a static field. The enum recipe is shorter: one constant, one object. Registry.INSTANCE is the same object everywhere.

enum Registry {
    INSTANCE;
    private final Map<String, String> data =
        new HashMap<>();
    void put(String k, String v) {
        data.put(k, v);
    }
}
🔮 Predict it

Same object, every time

What does this print?

enum Clock {
    INSTANCE;
    int ticks;
    int tick() { return ++ticks; }
}
void main() {
    Clock.INSTANCE.tick();
    Clock a = Clock.INSTANCE;
    System.out.println(a.tick());
}
  1. 2
  2. 1
  3. Compile error: enum fields must be final
Show the answer

Every Clock.INSTANCE refers to the same single object, so its ticks keeps growing: 1, then 2. (Enum fields don't have to be final, though shared mutable state deserves care.)

Thread-safe for free

Enum constants are created once, when the enum class is initialized. The JVM guards class initialization, so even if ten threads touch INSTANCE at the same moment, exactly one object is built — no locks, no double-checked tricks.

Classic vs enum singleton

✗ Classic
class Config implements Serializable {
    static final Config INSTANCE =
        new Config();
    private Config() { }
}

Reflection with setAccessible(true) can call the private constructor, and deserializing creates a second object unless you add readResolve().

✓ Enum
enum Config {
    INSTANCE;
}

Reflection refuses to create enum instances (IllegalArgumentException), and deserialization returns the existing constant.

⚠️ The trap

The one limitation

Every enum already extends **java.lang.Enum**, and Java allows only one superclass. So if your singleton must extend BaseService, the enum pattern is out. Interfaces are fine: enum Registry implements Store { INSTANCE; ... }.

💼 In the real world

Use it — sparingly

Enum singletons are great for stateless helpers, registries and caches in small apps and libraries. In big applications, frameworks like Spring manage single instances for you, which keeps them swappable in tests — global singletons are hard to fake.

Key takeaways

  1. enum Config { INSTANCE; ... } — one constant, one object
  2. Creation is thread-safe via class initialization
  3. Reflection can't instantiate enums; deserialization returns the same constant
  4. Limitation: an enum can't extend another class (interfaces are fine)
🤯 Did you know?

In Effective Java (2nd edition, 2008), Joshua Bloch called a single-element enum the best way to implement a singleton — and the advice still stands today.

Practice questions

What does this print?

enum Counter {
    INSTANCE;
    int count;
    int next() { return ++count; }
}
void main() {
    Counter.INSTANCE.next();
    Counter.INSTANCE.next();
    System.out.println(Counter.INSTANCE.next());
}
  1. 3
  2. 1
  3. 0
  4. Compile error: enum fields must be final
Check your answer

3. Every Counter.INSTANCE refers to the same object, so its count keeps growing: 1, 2, 3.

Which attack on a classic singleton (private constructor + static field) does an enum singleton block automatically?

  1. Calling the private constructor through reflection
  2. Calling the public getInstance() method
  3. Reading its fields through getters
  4. Passing the instance to another thread
Check your answer

Calling the private constructor through reflection. setAccessible(true) can open a classic private constructor, but reflection refuses to create enum instances (IllegalArgumentException).

Next: Java 16 lets you write a whole data class — constructor, getters, equals, hashCode, toString — in ONE line.