In Java, a singleton class is a class that allows only one instance of itself to be created and provides a global access point to that instance. Singleton patterns are often used in cases where a single shared resource, like a configuration manager or database connection pool, needs to be globally accessible.
Here’s how to create a singleton class in Java, including the commonly used “lazy initialization” and “eager initialization” methods.
1. Eager Initialization Singleton
In eager initialization, the singleton instance is created when the class is loaded. This is the simplest approach and is thread-safe without requiring synchronization.
public class SingletonEager {
// Create the singleton instance when the class is loaded
private static final SingletonEager instance = new SingletonEager();
// Private constructor prevents instantiation from other classes
private SingletonEager() {}
// Public method to provide access to the instance
public static SingletonEager getInstance() {
return instance;
}
}
2. Lazy Initialization Singleton
In lazy initialization, the singleton instance is created only when it’s needed. This can save resources if the instance is not always required, but it requires synchronization to make it thread-safe.
public class SingletonLazy {
// Instance is not created initially
private static SingletonLazy instance;
// Private constructor prevents instantiation from other classes
private SingletonLazy() {}
// Public method to provide access to the instance, with synchronized access
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
3. Enum Singleton
In Java, using an enum is a simple, effective way to create a singleton. Enum singletons are thread-safe, provide serialization out of the box, and prevent additional instances from being created, even during deserialization.
public enum Logger {
INSTANCE; // The single instance of the singleton
// Private constructor - automatically called once when the INSTANCE is created
Logger() {
System.out.println("Logger instance created.");
}
// Method to log messages
public void log(String message) {
System.out.println("Log: " + message);
}
// Method to log error messages
public void error(String message) {
System.out.println("Error: " + message);
}
}
public class Main {
public static void main(String[] args) {
// Access the singleton instance
Logger logger = Logger.INSTANCE;
// Use the logger to log messages
logger.log("Application started");
logger.error("An error occurred");
// Access the singleton instance again
Logger anotherLogger = Logger.INSTANCE;
// Demonstrate that both references point to the same instance
System.out.println("Are both instances the same? " + (logger == anotherLogger));
}
}
Logger instance created.
Log: Application started
Error: An error occurred
Are both instances the same? true
Summary
Each of these methods has its own advantages:
- Eager Initialization: Simple and thread-safe but may create instances unnecessarily.
- Lazy Initialization: Saves resources by creating an instance only when needed, but requires synchronization.
- Enum Singleton: The most effective, thread-safe approach that handles serialization and reflection automatically.