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.
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
- Controlled creation: The class can prevent duplicate instances.
- Shared access: Callers can access one coordinated resource.
- Hidden dependencies: Global access can make a class's requirements less visible.
- Testing difficulty: Shared state can leak between tests and make isolation harder.
- Concurrency risks: Mutable Singleton state requires careful thread-safety design.
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.