Singleton Pattern in Java: Simple Guide with Easy Examples (2026)

Singleton Pattern in Java: Simple Guide with Easy Examples (2026)

Singleton Pattern in Java: Simple Guide with Easy Examples

Singleton is the first design pattern most people learn. It sounds fancy, but the idea is very simple — one class, one object, shared by everyone. In this guide, I will explain it in plain words, show you a real-life example, and then show you how easy it is to accidentally break it. By the end, you will understand it better than most working developers.

What is Singleton, in simple words?

Imagine you are building a Java program. You have a class called Logger that writes messages to a file.

Now imagine your program uses 50 different classes, and each one creates its own Logger object. That means 50 loggers, all writing to the same file at the same time. The result? A messy log file with jumbled lines. You cannot tell which message came from which class.

What if there was only one Logger object? All 50 classes share it. Everyone writes to the same object. The log stays clean and in order.

That is the whole idea of Singleton. One class, one object, shared everywhere.

Here is what Singleton actually means:

  • You can never create more than one object of this class
  • Anyone in the program can ask for the object and get the same one
  • Only the class itself can create the object — nobody outside

How do we stop people from creating new objects? We make the constructor private. That means new Logger() will not work outside the class. The only way to get the object is through a special static method that returns the one shared instance.

That is the entire concept. Simple, right?

A real-life example you already know

Most tutorials use the "one president per country" example. You have probably read it a hundred times. Let me give you a better one that you see every day.

Think about a traffic signal at a big crossing.

There is exactly one traffic signal at that crossing. Not two. Not zero. One.

Why? Because if there were two signals — one for one direction, one for the other — and they did not match, cars would crash into each other. Only one signal can control the whole crossing.

That signal works like a Singleton because:

  • Every driver and pedestrian can see it. (Everyone can access it.)
  • No one can put up a second one without permission. (Private constructor.)
  • Its color — green, yellow, red — is shared by everyone watching. (One shared state.)

Now think about this. What if someone secretly puts up a second signal? Maybe on the other side of the crossing, hidden from view. Now drivers see two different lights. Chaos.

That is exactly what happens when someone breaks Singleton. Your program ends up with two "traffic signals" that do not agree with each other. Bugs appear that are very hard to find. Keep this picture in mind — we will come back to it.

5 ways people write Singleton

Over the years, Java developers have invented five common ways to write a Singleton. Each one fixed a problem with the previous one. But each one also created a new small problem. Let's look at all five.

1. Eager initialization — create it right away

public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

The object is created the moment the class is loaded into memory. Simple. No threading issues. No tricks.

But — what if nobody ever uses it? You wasted memory and time for nothing. Fine if the object is small. Bad if it opens a file, connects to a database, or does something expensive.

2. Lazy initialization — create it when needed

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

Now the object is created only when someone first asks for it. Saves memory. But there is a big problem: if two threads ask for the object at the same time, both will see "null" and both will create their own object.

Now you have two Singletons. That is a bug, and it is very hard to catch because it only happens sometimes.

3. Synchronized — the slow but safe way

public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;

    private SynchronizedSingleton() {}

    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}

Adding the word synchronized means only one thread can enter the method at a time. This fixes the threading problem.

But now every single call to getInstance() has to wait in line. Even after the object is created. If you call this method a thousand times a second, it slows your program down.

4. Double-checked locking — fast and safe

public class DoubleCheckedSingleton {
    private static volatile DoubleCheckedSingleton instance;

    private DoubleCheckedSingleton() {}

    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}

This checks twice. First check is fast — if the object already exists, no need to lock. Second check happens only the first time, inside a lock, to make sure only one thread creates the object.

The word volatile is important. Without it, the Java Virtual Machine might hand out a half-built object. Do not skip it.

5. Static holder — clean and clever

public class HolderSingleton {
    private HolderSingleton() {}

    private static class Holder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}

This is clever. The inner class Holder is not loaded until the first time you call getInstance(). So the object is created lazily. But Java already knows how to load classes safely, so no synchronized needed.

Best of both worlds. Until Java 5 arrived, this was the smartest way to write a Singleton.

When to use it and when not to

Singleton is useful, but it is also easy to misuse. Here is the honest version.

Good times to use Singleton

  • Logger — one log file, one writer, no jumbled messages
  • Configuration settings — one source of truth for the whole app
  • Database connection pool — one pool that manages all connections
  • Cache — one shared memory area for everyone
  • Hardware control — one object controlling a printer, a scanner, or a device

Bad times to use Singleton

  • "I want to share data between classes" — that is not what Singleton is for
  • "I am too lazy to pass parameters" — that is not good design
  • "I want to save memory" — creating objects in Java is cheap. Do not over-optimize.
  • "It is easy to write" — easy is not the same as correct

Here is a simple rule. If you cannot explain why having two objects would cause a problem, do not use Singleton.

It is not a fancy shortcut for global variables. It is a solution for a very specific kind of problem.

5 ways to break Singleton

Now the interesting part. Singleton looks solid, but it has cracks. Here are the five ways someone can break it. And what happens when they do.

1. Reflection — using Java's back door

Java has a feature called reflection that lets you look inside any class and call its private methods. Including the private constructor.

Constructor<LazySingleton> ctor = LazySingleton.class.getDeclaredConstructor();
ctor.setAccessible(true);
LazySingleton second = ctor.newInstance();
// Now you have two Singleton objects. Oops.

This is the most common way to break Singleton. And it works on every version that uses a private constructor.

2. Serialization — saving and loading

If your Singleton class implements Serializable, you can save it to a file and load it back. But when you load it, Java creates a new object. Not the one that already existed.

// Save to file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("s.ser"));
out.writeObject(LazySingleton.getInstance());

// Load from file
ObjectInputStream in = new ObjectInputStream(new FileInputStream("s.ser"));
LazySingleton loaded = (LazySingleton) in.readObject();

// loaded is NOT the same object as LazySingleton.getInstance()
// You now have two Singletons again

This is sneaky because it works even when everything looks fine.

3. Cloning — making a copy

If your Singleton class implements Cloneable, you can call clone() on it. That creates a copy — bypassing the private constructor entirely.

LazySingleton cloned = (LazySingleton) LazySingleton.getInstance().clone();
// cloned is a new object

Most developers do not implement Cloneable on a Singleton. But if you do, you have left the door open.

4. Multi-threading — two threads race

This one only breaks the naive lazy version. Two threads both check if (instance == null), both see null, and both create a new object. Now you have two.

This is why the synchronized and double-checked versions exist. They prevent this race.

5. Multiple class loaders — advanced, but real

In big enterprise servers, the same class can be loaded two different times by two different "loaders." Each loader has its own memory, so each gets its own Singleton object.

This one is rare. You probably will not hit it in a simple Java or Spring Boot project. But if you work on plugins or big servers, it happens.

How to stop each break

Fix for reflection

Add a check inside the constructor. If an object already exists, throw an error.

private LazySingleton() {
    if (instance != null) {
        throw new RuntimeException("Please use getInstance()");
    }
}

This works. But it feels ugly to throw an error inside a constructor. There is a cleaner way — the enum solution. We will see it below.

Fix for serialization

Add a method called readResolve(). Java calls it automatically when loading a saved object. If you return the existing instance, Java uses that instead.

protected Object readResolve() {
    return getInstance();
}

Four lines. Solves the whole problem. The trick is remembering to add it.

Fix for cloning

Override clone() and make it throw an error. Or better — just do not implement Cloneable at all. Then no one can clone.

@Override
protected Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException("Singleton cannot be cloned");
}

Fix for multi-threading

Use the double-checked or static holder version. Both are safe against threading issues without slowing your program down.

Fix for multiple class loaders

This one is hard to fix properly. In most normal applications, you do not need to worry about it. In big server applications, the fix is to use a shared loader or a service registry pattern. Complex stuff — skip until you actually need it.

The easiest and best way: Enum Singleton

In 2003, a developer named Joshua Bloch wrote a famous book called Effective Java. In it, he said something strange: the best way to write a Singleton is to use an enum.

People thought he was joking. Then they tried it. Then they stopped joking.

public enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something");
    }
}

That is it. That is the whole Singleton. To use it:

EnumSingleton.INSTANCE.doSomething();

Now here is the amazing part. This tiny piece of code solves every single break problem we just talked about:

  • Reflection does not work — Java itself blocks creating enum objects through reflection
  • Serialization works correctly — Java treats enums specially and always returns the same instance
  • Cloning does not work — enums cannot be cloned, Java throws an error
  • Threading is safe — the Java Virtual Machine creates enum objects only once
  • Lazy loading works — the enum is loaded when first used, not at startup

Five problems. One solution. One line of code.

The only limitation: your enum cannot extend another class. Java does not allow that. So if you need inheritance, use the static holder version instead.

Where Singleton is used in real Java code

You have probably used several Singletons today without realizing it. Here are some real examples from Java and popular frameworks:

  • Runtime.getRuntime() — the running Java program itself is a Singleton
  • System.getSecurityManager() — one security manager for the whole program
  • Desktop.getDesktop() — one desktop environment for your operating system
  • Spring framework beans — by default, every bean in Spring is a Singleton. The whole framework is built on this idea.
  • Loggers in Log4j and Logback — one logger per logger name, so logs stay clean
  • Hibernate SessionFactory — typically one per application, because it is expensive to create

Notice something? Every single one of these is a genuinely single resource. That is the pattern working perfectly.

Common questions

What is Singleton pattern in simple words?

Singleton is a design pattern that makes sure a class has only one object in the entire program. Every part of the program shares that one object instead of creating new ones.

Why do we need Singleton in Java?

We need Singleton when having two objects would cause problems. For example, two log files writing at the same time will mix up your logs. Two connection pools will waste database connections.

How many ways can we break Singleton?

There are five common ways to break Singleton: using Reflection, Serialization, Cloning, Multi-threading, and multiple ClassLoaders. Using enum is the simplest way to prevent all of them.

Which is the best way to write Singleton in Java?

The enum Singleton is the best way. It is thread-safe, cannot be broken by reflection or serialization, and needs only one line of code. It is the cleanest solution.

Is Singleton bad to use?

Not bad, but easy to misuse. Use it only when you really need one shared object. Do not use it just to avoid passing parameters or to share data between classes.

What is the difference between lazy and eager Singleton?

Eager Singleton creates the object when the class loads, even if nobody uses it. Lazy Singleton creates the object only when someone first asks for it. Lazy saves memory but needs careful threading.

Where to go next

You now know Singleton better than most working developers. You understand why it exists, the five ways to write it, the five ways to break it, and the one-line enum solution that fixes everything.

If you remember only one thing, remember this: Singleton itself is not hard. Most versions of it that people write are.

If you can use an enum, use an enum. If you cannot, use the static holder. Everything else is a compromise.

If this helped, bookmark it. But the real learning happens when you open your IDE and type the code yourself. Try writing a Singleton, then try breaking it. That is how it sticks.