Lazy initialization in a Singleton means the single instance is not created when the class is loaded, but only the first time it's actually requested. This defers the cost of construction until the object is genuinely needed.
Key Points: • The instance field starts as null and is only assigned inside the accessor method (e.g., getInstance()) on first use. • It's useful when construction is expensive — heavy configuration loading, opening connections, or large object graphs. • Naive lazy initialization is not thread-safe by default, since two threads could both see a null instance and create two objects. • Common fixes include synchronized getInstance(), double-checked locking with a volatile field, or the initialization-on-demand holder idiom. • The alternative, eager initialization, creates the instance at class-loading time, trading startup cost for simplicity and inherent thread safety.
Example: A DatabaseConnectionManager singleton that opens an expensive connection pool benefits from lazy initialization so that classes which never touch the database don't pay that startup cost.
Code Example:
public class ConfigManager {
private static volatile ConfigManager instance;
private ConfigManager() {
// expensive setup here
}
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
}Interview Tip: A concise interview answer is:
"Lazy initialization means the singleton instance is only created the first time getInstance() is called, not at class load time, which saves resources if the object is expensive to construct and might never be used. The catch is you need double-checked locking or a holder class to make that lazy creation thread-safe."