In Java, a Singleton Class is a class that allows only one instance of itself to be created and provides a global point of access to that instance. The Singleton pattern is a design pattern that ensures that a class has only one instance and provides a way to access it. It is used when you need to control access to shared resources, such as a configuration manager, database connection pool, or logging class, and you want to avoid the overhead of creating multiple instances.
In this guide, we will explore the concept of a Singleton class, how to implement it, its benefits, drawbacks, and best practices.
A Singleton Class is a class designed to restrict instantiation to only one object. This means that only one instance of the class will ever be created throughout the entire lifecycle of the application. The Singleton pattern is useful for managing global resources or providing a central point of control.
There are several ways to implement the Singleton pattern in Java, each with its own advantages and drawbacks. Let's look at the most commonly used approaches:
In eager initialization, the Singleton instance is created at the time of class loading. This is a simple and thread-safe approach, but it may create an instance even if it is never used, leading to wasted resources.
public class Singleton {
// Create the single instance eagerly
private static final Singleton instance = new Singleton();
// Private constructor to prevent instantiation
private Singleton() {}
// Public method to provide access to the instance
public static Singleton getInstance() {
return instance;
}
}
Advantages:
Disadvantages:
In lazy initialization, the Singleton instance is created only when it is needed (i.e., when getInstance()
is called for the first time). This saves resources if the instance is never used.
public class Singleton {
// Instance is created only when needed
private static Singleton instance;
// Private constructor
private Singleton() {}
// Public method to provide access to the instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // Instance is created lazily
}
return instance;
}
}
Advantages:
Disadvantages:
Double-checked locking improves lazy initialization by ensuring that the instance is created only once, even in a multi-threaded environment, and without the overhead of synchronization after the instance is created.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Advantages:
Disadvantages:
This is considered the most efficient and preferred approach to Singleton pattern implementation. It uses the static inner class
to implement the Singleton instance. The class is loaded only when it is needed, ensuring lazy initialization without any synchronization issues.
public class Singleton {
// Static inner class responsible for creating the Singleton instance
private static class SingletonHelper {
private static final Singleton INSTANCE = new Singleton();
}
// Private constructor
private Singleton() {}
// Public method to provide access to the instance
public static Singleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
Advantages:
Disadvantages:
Here are the general steps to implement a Singleton class:
readResolve()
to maintain the Singleton property during deserialization.Here’s a simple example of implementing a Singleton class using the Bill Pugh Singleton Design:
public class Singleton {
// Static inner class responsible for creating the Singleton instance
private static class SingletonHelper {
private static final Singleton INSTANCE = new Singleton();
}
// Private constructor to prevent instantiation
private Singleton() {}
// Public method to provide access to the instance
public static Singleton getInstance() {
return SingletonHelper.INSTANCE;
}
// Method to test Singleton functionality
public void showMessage() {
System.out.println("Hello from Singleton!");
}
}
public class Main {
public static void main(String[] args) {
// Getting the only instance of Singleton class
Singleton singleton = Singleton.getInstance();
singleton.showMessage(); // Output: Hello from Singleton!
}
}
Use the Singleton pattern when: