Answer
Singleton Design Pattern in Java
Singleton ensures only one instance of a class exists throughout the application and provides a global access point to it.
Basic Singleton (NOT Thread-Safe)
Java
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {
System.out.println("Connecting to database...");
}
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection(); // ⚠️ Not thread-safe
}
return instance;
}
}
Thread-Safe Implementations
1. Synchronized Method (simple but slow)
Java
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
// Problem: synchronized on every call — performance overhead
}
2. Double-Checked Locking (Best for most cases)
Java
public class DatabaseConnection {
// volatile ensures visibility across threads
private static volatile DatabaseConnection instance;
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
if (instance == null) { // First check (no lock)
synchronized (DatabaseConnection.class) {
if (instance == null) { // Second check (with lock)
instance = new DatabaseConnection();
}
}
}
return instance;
}
}
3. Enum Singleton (Simplest + Thread-Safe + Serialization-Safe)
Java
public enum WebDriverManager {
INSTANCE;
private WebDriver driver;
public WebDriver getDriver() {
if (driver == null) {
driver = new ChromeDriver();
}
return driver;
}
public void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
// Usage
WebDriver driver = WebDriverManager.INSTANCE.getDriver();
4. Initialization-on-Demand Holder (Lazy, Thread-Safe, No sync)
Java
public class ConfigManager {
private final Properties properties;
private ConfigManager() {
properties = new Properties();
try {
properties.load(new FileInputStream("config.properties"));
} catch (IOException e) {
throw new RuntimeException("Config not found", e);
}
}
// Inner class — loaded only when first accessed
private static class Holder {
static final ConfigManager INSTANCE = new ConfigManager();
}
public static ConfigManager getInstance() {
return Holder.INSTANCE; // thread-safe without synchronized
}
public String get(String key) {
return properties.getProperty(key);
}
}
Singleton in Automation Frameworks
Java
// WebDriver Singleton — one browser per test class
public class BrowserFactory {
private static volatile WebDriver driver;
private BrowserFactory() {}
public static WebDriver getDriver() {
if (driver == null) {
synchronized (BrowserFactory.class) {
if (driver == null) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();
}
}
}
return driver;
}
public static void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
// Any test can call
WebDriver driver = BrowserFactory.getDriver();
Breaking Singleton (and Prevention)
Java
// Reflection can break singleton:
Constructor<Singleton> c = Singleton.class.getDeclaredConstructor();
c.setAccessible(true);
Singleton s2 = c.newInstance(); // breaks singleton!
// Prevention — throw exception in constructor if instance exists
private Singleton() {
if (instance != null) {
throw new RuntimeException("Use getInstance()");
}
}
// Or use Enum — reflection-proof
