Ensure a class has exactly ONE instance and provide a global point of access.
1. Eager Initialization (Simple, Safe)
Static initialization creates the instance immediately during class loading. It is simple, thread-safe, and perfect if the instance is cheap or always used. To be absolutely resilient against reflection/serialization attacks out of the box, use an **Enum Singleton**.
2. Lazy Initialization (Thread-Safe)
Use when initialization is expensive (reads from disk, connects to DB) and you may not always need it. For senior/production-ready applications, the implementation must prevent **Reflection attacks**, **Serialization bypasses**, and **Cloning bypasses**, while securing thread-safety via **Double-Checked Locking (DCL)** with a volatile field, or the static **Bill Pugh Holder Idiom**.
// ─── DOUBLE-CHECKED LOCKING SINGLETON (SENIOR PATTERN) ──────────────────────
// Thread-safe, high-performance, and resilient against typical bypass vectors:
// 1. Reflection Attacks (guards in constructor)
// 2. Serialization Attacks (defines readResolve())
// 3. Cloning Attacks (overrides clone())
// ────────────────────────────────────────────────────────────────────────────
import java.io.Serializable;
final class DatabaseConnectionPool implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
// volatile is CRITICAL: prevents instruction reordering.
// Without volatile, a thread may observe a non-null, uninitialized reference:
// Memory Allocated -> Assigned to Reference -> Constructor Executes (Reordered!)
private static volatile DatabaseConnectionPool instance;
private DatabaseConnectionPool() {
// Reflection defense: prevent instantiation via Constructor.newInstance()
if (instance != null) {
throw new IllegalStateException("Instance already initialized. Use getInstance().");
}
System.out.println(" [DatabaseConnectionPool] Expensive resource initialization...");
}
public static DatabaseConnectionPool getInstance() {
DatabaseConnectionPool localRef = instance; // Use local variable for 25% performance boost
if (localRef == null) {
synchronized (DatabaseConnectionPool.class) {
localRef = instance;
if (localRef == null) {
instance = localRef = new DatabaseConnectionPool();
}
}
}
return localRef;
}
// Serialization defense: returns the existing instance during deserialization
protected Object readResolve() {
return getInstance();
}
// Cloning defense: prevent cloning
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton cloning is prohibited.");
}
}
public class Main {
public static void main(String[] args) {
DatabaseConnectionPool pool1 = DatabaseConnectionPool.getInstance();
DatabaseConnectionPool pool2 = DatabaseConnectionPool.getInstance();
System.out.println(" [Main] Same instance? " + (pool1 == pool2)); // Should output: true
}
}