Eager vs Lazy Initialization in OOP
Eager vs lazy initialization explained — startup cost, thread safety and the double-checked locking pattern, with a Java example.
Expected Interview Answer
Eager initialization creates an object’s dependencies or resources up front, at declaration or construction time, while lazy initialization defers creation until the value is first actually needed.
Eager initialization is simple and thread-safe by construction because the object exists before any other thread can observe a half-built state, but it pays the creation cost even when the resource is never used and can slow down application startup. Lazy initialization avoids that upfront cost by creating the object only on first access, which helps when the resource is expensive (a database connection, a large cache) or conditionally used, but a naive lazy implementation is not thread-safe and needs synchronization, double-checked locking, or a holder-class idiom to be safe under concurrency. The choice is a tradeoff between predictable startup cost and deferred, conditional cost with added complexity.
- Eager: simple, thread-safe by default, predictable startup
- Lazy: saves memory and time when the resource may not be used
- Lazy: supports faster application startup for heavy dependencies
- Eager: no risk of first-access race conditions
AI Mentor Explanation
Eager initialization is a ground staff preparing all twenty-two possible pitches before the season even starts, whether or not each one is ever used for a match. Lazy initialization is preparing a pitch only the week its scheduled match approaches, saving effort on pitches that end up unused due to rain-outs. Both approaches deliver a usable pitch when needed, but one pays the preparation cost upfront for every pitch while the other pays it only for pitches actually required, and only when the need becomes real.
Step-by-Step Explanation
Step 1
Identify the cost driver
Determine whether the object is expensive to create (I/O, memory) or cheap and always used.
Step 2
Choose eager for cheap/always-used
If the object is small and always needed, initialize it at declaration for simplicity.
Step 3
Choose lazy for expensive/conditional
If the object is costly and only sometimes used, defer creation to first access via a getter check.
Step 4
Guard lazy paths for concurrency
Use synchronization, double-checked locking, or a static holder class so lazy creation is thread-safe.
What Interviewer Expects
- A clear tradeoff statement: startup cost vs first-access cost
- Awareness that naive lazy initialization is not thread-safe
- Mention of the holder-class idiom or double-checked locking as a fix
- A real scenario where each approach is the right default
Common Mistakes
- Assuming lazy initialization is always better because it “saves memory”
- Writing a lazy getter with no synchronization in a multithreaded context
- Believing eager initialization always slows startup meaningfully
- Confusing lazy initialization with the broader Lazy Loading design pattern without distinguishing scope
Best Answer (HR Friendly)
“Eager initialization creates an object right away, when the program starts or the containing object is built, so it is simple and always ready but pays the cost even if unused. Lazy initialization waits until the object is actually needed before creating it, which saves resources for things that might not be used, at the cost of extra complexity to make that first creation safe under concurrent access.”
Code Example
class EagerSingleton {
// Created immediately when the class loads
private static final EagerSingleton INSTANCE = new EagerSingleton();
public static EagerSingleton getInstance() { return INSTANCE; }
}
class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() { }
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton(); // created only on first call
}
}
}
return instance;
}
}Follow-up Questions
- What is double-checked locking and why does the field need to be volatile?
- How does the initialization-on-demand holder idiom avoid synchronization entirely?
- When would lazy initialization hurt more than help?
- How does Spring choose between eager and lazy bean initialization?
MCQ Practice
1. The main risk of a naive lazy-initialized getter in a multithreaded program is?
Without synchronization, two threads can both see a null field and each create their own instance, breaking the singleton guarantee.
2. Eager initialization is generally preferred when?
For cheap, always-used objects, eager initialization is simpler and avoids any lazy-access synchronization overhead.
3. What keyword is required on a lazily initialized field used with double-checked locking in Java?
volatile prevents instruction reordering so other threads never observe a partially constructed object.
Flash Cards
Eager initialization in one line? — Create the object immediately, at declaration or construction time.
Lazy initialization in one line? — Defer object creation until the value is first actually needed.
Biggest lazy-init risk? — Race conditions on first access if not properly synchronized.
Safe lazy pattern for singletons? — Double-checked locking with volatile, or the static holder-class idiom.