W3docs

Java Singleton Pattern

Implement the singleton design pattern in Java safely with eager, lazy, and enum-based approaches.

The singleton pattern restricts a class to a single instance and provides a global access point to it. A logging facade, a configuration registry, an in-process cache — these are the kinds of things that fit. In Java, the pattern has a handful of standard shapes, each with its own thread-safety and lazy-loading trade-offs. There's also one approach that quietly avoids most of the trouble.

A quick word of caution first. "Singleton" is famously easy to overuse — every singleton is, in effect, a global, and globals make code harder to test and to reason about. Most modern Java apps prefer dependency injection: the framework wires up a single instance and hands it to the components that need it, without any of them having to call Foo.getInstance(). Reach for an explicit singleton when DI isn't on the table or genuinely overkill.

What every singleton needs

Any singleton implementation shares three pieces:

  • A private constructor, so no one outside the class can call new.
  • A private static field holding the single instance.
  • A public static accessor that returns it.

The variations are mostly about when the instance gets created and how the access stays thread-safe.

Eager initialization

The simplest form creates the instance when the class loads:

public final class Eager {
  private static final Eager INSTANCE = new Eager();
  private Eager() {}
  public static Eager getInstance() { return INSTANCE; }
}

Class initialization in the JVM is guaranteed to be thread-safe and to run exactly once, so INSTANCE is set safely with no locks. Use this when:

  • Construction is cheap, or you know you'll always need the instance.
  • You don't mind paying the cost at class-load time.

Lazy initialization with double-checked locking

If construction is expensive and you might never need the instance, you can defer it. The naive lazy version isn't thread-safe; the correct one uses double-checked locking with volatile:

public final class Lazy {
  private static volatile Lazy instance;
  private Lazy() {}

  public static Lazy getInstance() {
    Lazy local = instance;       // local read avoids re-reading the volatile field
    if (local == null) {
      synchronized (Lazy.class) {
        local = instance;
        if (local == null) {
          local = new Lazy();
          instance = local;
        }
      }
    }
    return local;
  }
}

volatile is essential — without it, another thread could see the field set to a non-null reference whose constructor hasn't finished. Cumbersome, but correct.

The initialization-on-demand holder

The cleanest lazy form uses a private nested class. The JVM only loads the holder when someone first calls getInstance(), so the work is deferred — and the JVM's class-init guarantees handle the thread safety:

public final class Holder {
  private Holder() {}
  private static class H {
    private static final Holder INSTANCE = new Holder();
  }
  public static Holder getInstance() { return H.INSTANCE; }
}

No synchronized, no volatile, no double-checked locking — and still lazy. This is the lazy form to use in most code.

The enum singleton

The shortest correct singleton in Java is an enum with one constant:

public enum Config {
  INSTANCE;
  public String get(String key) { /* ... */ }
}

Config.INSTANCE is the singleton. Joshua Bloch's recommendation (Effective Java, Item 3) is that this is the best singleton implementation, because the JVM guarantees:

  • Exactly one instance. Enums are constructed exactly once per JVM.
  • Thread-safe construction. Same class-init guarantees as the holder pattern.
  • Reflection-safe. Reflection can't invoke an enum's constructor; ordinary singletons can be defeated by Constructor.setAccessible(true).
  • Serialization-safe. Deserializing a normal singleton can quietly produce a second instance unless you handle readResolve. Enums are immune.

The only thing it can't do is extend another class — enums implicitly extend java.lang.Enum. It can still implement interfaces.

Things that break naive singletons

Watch for these — they're the reason "just use a static field" isn't always enough:

  • Multiple class loaders. A singleton is one-per-classloader, not one-per-JVM. In containers that isolate apps with their own loaders, the same class can have several "the" instances.
  • Reflection. setAccessible(true) plus Constructor.newInstance() can build a second instance of any non-enum singleton. Guard the constructor with if (INSTANCE != null) throw ... if this is a real concern.
  • Serialization. A Serializable singleton needs private Object readResolve() { return INSTANCE; } to avoid producing a second copy on each deserialization.
  • Testing. Singletons are notoriously hard to stub or reset. Prefer dependency injection in code you expect to unit-test.

A worked example

java— editable, runs on the server

What's next

That wraps up Part 6 and the whole tour of object-oriented Java — from classes, inheritance, and polymorphism through interfaces, enums, records, sealed hierarchies, and the methods every object inherits. The next part zooms out to how Java code is organized: namespaces, the file-system layout that mirrors them, and the import machinery that pulls types from other places into yours. Continue to Java packages.

Practice

Practice

Why is the enum singleton (`enum X { INSTANCE; ... }`) usually considered the best Java singleton implementation?