Creational Design Pattern

Singleton Design Pattern in Java

Ensure that a class has only one instance and provide a global access point to that instance.

In this lesson: Learn the Singleton structure, common Java implementations, thread safety, and the design trade-offs to consider before using it.

What is the Singleton Design Pattern?

The Singleton pattern restricts a class to one managed instance during the lifetime of an application. The class controls its own construction and exposes a method through which other code can access the shared instance.

Singletons can be useful for genuinely shared, stateless services or application-wide resources. They should be used carefully because global access can make dependencies harder to see and tests harder to isolate.

Basic Singleton implementation

A private constructor prevents other classes from creating objects directly. A private static field stores the instance, and a public static method returns it.

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

    private AppConfig() {
    }

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

This eager implementation creates the instance when the class is initialized. Class initialization is thread-safe, which makes this approach simple and reliable.

Lazy initialization

Lazy initialization creates the instance only when it is first requested. A basic lazy implementation is not safe when multiple threads call the accessor at the same time.

public final class LazyLogger {
    private static LazyLogger instance;

    private LazyLogger() {
    }

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

For concurrent applications, prefer eager initialization, an initialization-on-demand holder, or an enum Singleton instead of adding synchronization without understanding its cost.

Thread-safe Singleton options

The initialization-on-demand holder uses Java class initialization guarantees while keeping creation lazy.

public final class Settings {
    private Settings() {
    }

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

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

This approach is lazy, thread-safe, and does not synchronize every call to getInstance().

Enum Singleton

An enum can provide a concise Singleton implementation. Java guarantees that each enum constant is created once, and enum serialization and reflection behavior are safer than many hand-written implementations.

public enum ApplicationRegistry {
    INSTANCE;

    public void register(String name) {
        // Register an application component.
    }
}

Use an enum when the Singleton does not need to extend another class and its API fits naturally as an enum constant.

Advantages and limitations

When should you use it?

Use Singleton only when one instance is a real business or technical requirement, such as a process-wide registry with controlled state. In many Spring Boot applications, dependency injection is a better choice because the container manages a default single bean while keeping dependencies explicit and testable.

Next step: Continue with the Design Patterns course and learn the Factory Method pattern.