Singleton Class
Singleton Class
A school program needs reliable data and clear rules. A singleton design provides one shared instance through controlled access. This can represent a process-wide school configuration, but shared state still needs careful design.
Understand
A singleton design provides one shared instance through controlled access.
Apply
Trace the Java example, predict its output and explain the result before running it.
Avoid mistakes
A simple lazy singleton is not automatically thread-safe. An enum instance is a concise Java option; one instance does not imply immutable state.
Singleton Class
In Java, a Singleton class is a class that allows only one instance of itself to be created—and gives access to that single instance. It’s a widely used design pattern, especially when you need exactly one object to coordinate actions across the system.
Why Use a Singleton?
Imagine you're logging actions throughout your application. Would you want multiple loggers writing randomly? Of course not. A singleton logger ensures consistent behavior and state—no matter where it’s used.
- Ensures controlled access to shared resources
- Saves memory by avoiding multiple instances
- Promotes centralized management (e.g., database connectors, config managers)
How to Implement a Singleton Class in Java
Let’s go through different ways to implement a singleton class. Each method has its own nuances, especially in multi-threaded environments.
1. Eager Initialization
The instance is created at the time of class loading.
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
System.out.println("Instance created");
}
public static EagerSingleton getInstance() {
return instance;
}
}
Usage
public class Main {
public static void main(String[] args) {
EagerSingleton obj1 = EagerSingleton.getInstance();
EagerSingleton obj2 = EagerSingleton.getInstance();
System.out.println(obj1 == obj2); // true
}
}
Instance created
true
Explanation
Only one object is created. Even if we try to get the instance multiple times, we get the same reference.
2. Lazy Initialization
In this approach, the instance is created only when it’s needed.
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
System.out.println("Instance created");
}
return instance;
}
}
Usage
public class Main {
public static void main(String[] args) {
LazySingleton obj1 = LazySingleton.getInstance();
LazySingleton obj2 = LazySingleton.getInstance();
System.out.println(obj1 == obj2); // true
}
}
Instance created
true
Explanation
The object is created only when getInstance() is called the first time. It saves resources but is not thread-safe.
3. Thread-Safe Singleton (Synchronized Method)
This method ensures thread safety using synchronization.
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
System.out.println("Instance created");
}
return instance;
}
}
Explanation
The synchronized keyword ensures that only one thread can execute this method at a time. It's safe, but might reduce performance due to synchronization overhead.
4. Double-Checked Locking (Efficient Thread-Safety)
This is a more efficient way of making the singleton thread-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();
System.out.println("Instance created");
}
}
}
return instance;
}
}
5. Singleton Using Enum (Best Practice)
Using Enum is the simplest and most effective way to implement a singleton in Java. It prevents reflection and serialization issues.
public enum EnumSingleton {
INSTANCE;
public void show() {
System.out.println("Singleton using Enum");
}
}
Usage
public class Main {
public static void main(String[] args) {
EnumSingleton singleton = EnumSingleton.INSTANCE;
singleton.show();
}
}
Singleton using Enum
Common Mistakes to Avoid
- Leaving constructor public or protected
- Ignoring thread safety in multi-threaded applications
- Using reflection to break singleton (use Enum to prevent this)
Where is Singleton Used?
Singleton pattern is used across frameworks and applications:
- Database connections
- Logger classes
- Configuration managers
- File handlers
Important boundaries and mistakes
A simple lazy singleton is not automatically thread-safe. An enum instance is a concise Java option; one instance does not imply immutable state.
Try the school example
Before running this Java program, write down each line you expect to see. Identify the value, branch, object or exception responsible for each line. Then explain whether the following assumption is valid: A simple lazy singleton is not automatically thread-safe.
Java 17 · Main.java · complete practice program
public class Main {
enum SchoolConfig {
INSTANCE
}
public static void main(String[] args) {
System.out.println(SchoolConfig.INSTANCE == SchoolConfig.INSTANCE);
}
}
Now change one thing: Compare the enum instance with itself again. Predict the new behavior before checking the worked variation.
Worked answer and reasoning
Java 17 · Main.java · source repeated for this answer
public class Main {
enum SchoolConfig {
INSTANCE
}
public static void main(String[] args) {
System.out.println(SchoolConfig.INSTANCE == SchoolConfig.INSTANCE);
}
}
Expected output
trueA singleton design provides one shared instance through controlled access. This can represent a process-wide school configuration, but shared state still needs careful design.
A simple lazy singleton is not automatically thread-safe. An enum instance is a concise Java option; one instance does not imply immutable state.
Run with javac Main.java, then java Main. The declaration of Main names the class; main starts the example, and println writes a complete output line.
Worked variation: Compare the enum instance with itself again. Both references still identify the same instance.
Check your understanding
What to remember
A singleton design provides one shared instance through controlled access. This can represent a process-wide school configuration, but shared state still needs careful design. A simple lazy singleton is not automatically thread-safe. An enum instance is a concise Java option; one instance does not imply immutable state.
Related explanations: Java Variables, Java If Statement - Syntax and Examples, Java Class - Blueprint of Objects, Exceptions in Java Introduction.
Supporting documentation: Official Java learning guide and Java 17 language specification.
Reaching the recap and completing the quizzes are separate milestones.