Singleton Pattern
What the Singleton Pattern Is
The Singleton pattern restricts a class to a single instance and gives the rest of the system one global access point to that instance. You reach for it when exactly one object must coordinate actions across a program — a configuration store, a logging facility, or a pool of expensive resources.
The mechanism is small. A static method checks whether the instance already exists. If it does not, the method creates it; if it does, the method returns the existing reference. Because the constructor is private, no other code can bypass this gate and create a second instance.
The problem it solves
When many clients each need a shared resource, naive code lets each client construct its own copy. If every client opened its own database connection, the server would be swamped and connection management would descend into chaos. Worse, separate instances drift into inconsistent state. The Singleton collapses all of those copies into one shared object that every client uses.
Structure
The pattern has two collaborators. SingletonObject enforces the single-instance rule and holds the shared state. SingletonDemo is any client that asks SingletonObject for the instance and then uses it.
SingletonObject
instance— a private static field of typeSingletonObjectthat holds the one shared instance for the whole process.SingletonObject()— the constructor is private, so no outside code can callnewon it.getInstance()— a public static method that returns the single instance, creating it on first call if it does not yet exist. This is the global access point.showMessage()— a representative instance method, standing in for whatever real behavior the singleton exposes.
SingletonDemo
The demo class holds main(), the program entry point. It calls SingletonObject.getInstance() ("asks"), receives the shared instance back ("returns"), and invokes a method on it.
Implementation
Across languages the recipe is the same: make the constructor private so nobody can call new from outside, and expose a static method that hands back the cached instance. The version below is eager — the instance is built once when the class loads, stored in a final field, and simply returned on every call. Because the JVM guarantees class initialization happens once and in a thread-safe way, this variant is automatically safe under concurrency without any locking.
class SingletonObject {
// Private static instance, eagerly initialized
private static final SingletonObject instance = new SingletonObject();
// Private constructor to prevent direct instantiation
private SingletonObject() {}
// Public method to get the sole instance of the class
public static SingletonObject getInstance() {
return instance;
}
// Method to display a message
public void showMessage() {
System.out.println("Hello from Singleton Pattern!");
}
}
public class Solution {
public static void main(String[] args) {
// new SingletonObject(); // compile error: constructor is private
SingletonObject object = SingletonObject.getInstance();
object.showMessage();
}
}Trying to call new SingletonObject() from main would fail to compile — that is the private constructor doing its job. The only door in is getInstance().
Thread-safe lazy variants. The code above is eager: the instance is built when the class loads. If you need create-on-first-use instead, use the Bill Pugh singleton holder (a static nested class whose initializer runs only when referenced) or an enum. Double-checked locking (DCL) is the classic lazy + thread-safe attempt, but it is broken without volatile: instance = new Singleton() is three steps — allocate memory, run the constructor, then publish the reference into instance — and the compiler/JVM is permitted to reorder the last two (publish the reference before the constructor has finished), so a second thread reading instance outside the lock can see a non-null but partially-constructed object. volatile forbids that reordering and supplies the happens-before edge.
// BROKEN without volatile: a reader can see a half-built instance
private static Singleton instance; // missing volatile
// FIXED:
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // 1st check, no lock
synchronized (Singleton.class) {
if (instance == null) // 2nd check, locked
instance = new Singleton(); // volatile publish
}
}
return instance;
}Prefer the Bill Pugh holder idiom or an enum — both get laziness and thread-safety without this subtlety.
Two ways a class-based Singleton is silently broken (and why enum is immune)
A private constructor stops new at compile time. But the JVM has two runtime back doors that bypass it entirely, each silently creating a second instance and breaking the one-instance guarantee. These are a top-3 senior-level Singleton interview probe.
Attack 1: Deserialization
When you serialize a singleton and deserialize it, ObjectInputStream.readObject() creates a brand-new instance by bypassing the constructor entirely (it uses internal JVM mechanisms, not new). The result: two objects in the same JVM that both believe they are the singleton.
// Demonstrate the deserialization attack
SingletonObject s1 = SingletonObject.getInstance();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(s1);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SingletonObject s2 = (SingletonObject) in.readObject();
in.close();
System.out.println(s1 == s2); // false — two instances exist!Defense: readResolve(). The Java serialization spec checks whether the deserialized class defines a readResolve() method. If it does, the return value of that method replaces the newly created object. So the singleton class adds:
// Inside SingletonObject — must implement Serializable
private Object readResolve() {
return instance; // discard the deserialized copy, return the real singleton
}The deserialized duplicate is created momentarily but immediately discarded and garbage-collected. Every non-transient object field is also a hole: a crafted stream can capture a reference to the momentary pre-readResolve instance through such a field (Bloch’s “singleton stealer” attack, Effective Java Item 89), smuggling a second live reference that breaks the singleton. Make all instance fields transient — or prefer the enum singleton, which is immune.
Attack 2: Reflection
The Reflection API can make any constructor accessible at runtime, including private ones. A single call creates a second instance that completely bypasses getInstance():
// Demonstrate the reflection attack
SingletonObject s1 = SingletonObject.getInstance();
Constructor<SingletonObject> constructor =
SingletonObject.class.getDeclaredConstructor();
constructor.setAccessible(true); // bypass the private modifier
SingletonObject s2 = constructor.newInstance();
System.out.println(s1 == s2); // false — two instances exist!Defense: guard the constructor. The private constructor checks whether an instance already exists and throws if so. This turns the reflection path into a runtime error:
private SingletonObject() {
if (instance != null) {
throw new IllegalStateException(
"Singleton already constructed — use getInstance()");
}
}This is a convention-level guard, not an airtight one — a determined attacker using sun.misc.Unsafe or manipulating the static field can still circumvent it. But it catches accidental misuse and makes the intent unmistakable.
Why enum is immune to both
An enum singleton is the recommendation from Joshua Bloch (Effective Java, Item 3) precisely because the JVM itself enforces singleton semantics at a level no class-based trick can match:
- Serialization: The Java serialization specification (§1.12) special-cases enums — it serializes only the enum constant's name, and on deserialization looks up the existing constant by name via
Enum.valueOf(). No new instance is ever created. NoreadResolve()needed. - Reflection: The
Constructor.newInstance()method injava.lang.reflectcontains an explicit check: if the class is an enum, it throwsIllegalArgumentException("Cannot reflectively create enum objects"). The back door is hard-coded shut. - Thread safety: Enum constants are initialized during class loading, which the JVM guarantees happens exactly once and with full happens-before ordering. No
volatile, no locking, no holder idiom needed.
public enum DatabasePool {
INSTANCE;
private final HikariDataSource ds;
DatabasePool() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost/mydb");
config.setMaximumPoolSize(10);
ds = new HikariDataSource(config);
}
public Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
// Usage — clean, safe, no getInstance() ceremony
Connection conn = DatabasePool.INSTANCE.getConnection();The trade-off: when enum is NOT the answer
Enum singletons cannot extend a class (Java enums implicitly extend java.lang.Enum), so they are unsuitable when the singleton must inherit from a specific base class. They also do not support lazy initialization — the instance is created at class-loading time, period. If the singleton is expensive to construct and might never be used, the Bill Pugh holder idiom (lazy, class-based, thread-safe) is the better fit, guarded with readResolve() + the constructor check above.
| Variant | Thread-safe? | Lazy? | Reflection-safe? | Serialization-safe? | Can extend a class? |
|---|---|---|---|---|---|
Eager static final | Yes (class init) | No | No (needs constructor guard) | No (needs readResolve()) | Yes |
| Bill Pugh holder | Yes (class init of inner class) | Yes | No (needs constructor guard) | No (needs readResolve()) | Yes |
| Double-checked locking | Yes (with volatile) | Yes | No (needs constructor guard) | No (needs readResolve()) | Yes |
| Enum | Yes (class init) | No | Yes (JVM-enforced) | Yes (JVM-enforced) | No |
Where It Is Used
Configuration management. Settings load once at startup and stay consistent for the whole application lifecycle, with every part of the code reading from a single source.
Database connection pools. Rather than opening and closing connections constantly, a single pool object reuses a fixed set of connections, giving consistent, efficient access from anywhere in the app.
Hardware access management. For a printer or a graphics device, one coordinating object serializes access, prevents conflicts, and owns the initialization and shutdown routine.
Trade-offs
The Singleton's strengths and weaknesses are two sides of the same coin. The single global access point that makes it convenient is also a piece of hidden global state.
| Pros | Cons |
|---|---|
| Controlled access — exactly one instance, reached through one well-known method. | Global state — encourages shared mutable state that is hard to reason about and debug. |
Thread-safe without extra locking — the eager, final form shown above relies on the JVM's one-time class initialization, so no synchronized or double-checked locking is needed. | Testing friction — hard to mock or reset; state leaks across tests. |
| Shared-resource management — natural fit for pools, caches, and device handles. | Tight coupling & hidden dependencies — callers depend on the global directly, so refactoring away from it touches every call site. |
Singleton vs. Dependency Injection
The most common modern alternative is to create one instance and inject it wherever it is needed, rather than letting code reach out to a global accessor. Both give you a single shared object; the difference is who controls the wiring and how visible the dependency is.
| Dimension | Classic Singleton | Single instance via Dependency Injection |
|---|---|---|
| How callers get it | Call a global getInstance() from anywhere. | Receive it as a constructor or method parameter. |
| Dependency visibility | Hidden — the dependency is buried inside method bodies. | Explicit — it appears in the signature, so collaborators are obvious. |
| Testability | Hard to substitute a fake; shared state persists between tests. | Easy to pass a mock or stub; each test wires its own instance. |
| Lifetime control | Owned by the class itself; one per process, hard to scope. | Owned by the container/composition root; can be scoped per-request, per-thread, etc. |
| Cost | Zero framework, almost no boilerplate — but the coupling tax is paid later, during testing and refactoring. | Some upfront wiring (and often a DI container) — but the dependency graph stays explicit and changeable. |
Singleton vs. a static utility class
A common interview probe: "why not just a class of static methods and fields instead of a Singleton?" A static utility class (Math-style) also gives one shared, globally-reachable bundle of state — but it can never implement an interface or be passed as an object, so you cannot substitute a fake in tests, cannot inject it, and cannot swap the implementation at runtime (test double, per-environment variant). A Singleton is still an instance: it can implement an interface, be handed to collaborators as that interface, and be mocked. So the rule is: if the shared thing is pure stateless functions with no need for polymorphism or mocking, a static utility class is simpler; the moment you need to program to an interface, inject it, or substitute it in a test, you need an object — either a Singleton or (better) a single injected instance.
When NOT to use a Singleton
Avoid the classic Singleton when the object carries mutable business state, when the code needs to be unit-tested in isolation, or when you may eventually need more than one instance (per tenant, per request, per environment). In those cases, create a single instance at the composition root and inject it instead — you keep "one shared object" without baking a global access point into every class. Reserve the textbook Singleton for genuinely process-wide, effectively-immutable concerns like a logger or a configuration snapshot.
Summary
The Singleton pattern guarantees one instance of a class and a global way to reach it. Its benefits — controlled access, optional lazy initialization, and tidy shared-resource management — come paired with real costs: global state, testing friction, tight coupling, and hidden dependencies. Use it deliberately for process-wide, stable concerns, and prefer injecting a single shared instance when you need testability or flexible lifetimes. If you do use a class-based Singleton, defend against the deserialization and reflection back doors — or use an enum, which the JVM protects for you.
Recall question
Why is the eager static final Singleton thread-safe without synchronized, and when would you still prefer a lazy variant such as the Bill Pugh holder or an enum?
Answer: The JVM initializes a class exactly once, and that initialization is thread-safe by specification. The eager final field is set during that one-time initialization, so every caller sees the fully-constructed instance without locks. You still prefer lazy variants when the singleton is expensive to create and might never be used, or when you want instance creation to fail gracefully on first use rather than at class-loading time. In Java, an enum is the safest published singleton because it also defeats reflection and serialization attacks.
Source
Based on the Gang of Four Design Patterns: Elements of Reusable Object-Oriented Software (Gamma, Helm, Johnson, Vlissides), Joshua Bloch, Effective Java, Item 3 ("Enforce the singleton property with a private constructor or an enum type"), and the Bill Pugh singleton-holder idiom. See also Refactoring.Guru, "Singleton Pattern." Re-authored and deepened for this guide — added the deserialization and reflection attack demonstrations with concrete Java code, the defenses (readResolve + constructor guard), the enum immunity explanation with JVM-spec references, and the variant comparison table.
🤖 Don't fully get this? Learn it with Claude
Stuck on Singleton Pattern? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Singleton Pattern** (OO & Low-Level Design) and want to truly understand it. Explain Singleton Pattern from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Singleton Pattern** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Singleton Pattern** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Singleton Pattern** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.