Q114. What is the Singleton pattern, and how do you implement it thread-safely? Ensures a class has only one instance, with a global access point. Thread-safe implementations:
- Eager initialization: instance created at class loading (simple, always instantiated).
- Double-checked locking with a
volatilefield (lazy + efficient). - Enum singleton:
enum Singleton { INSTANCE; }— simplest, inherently thread-safe, and serialization-safe (recommended by Joshua Bloch). - Initialization-on-demand holder idiom: relies on class-loading guarantees for lazy, thread-safe init without synchronization overhead.
Q115. What is the Factory pattern? Encapsulates object creation logic in a separate method/class, decoupling client code from concrete classes. A Factory Method lets subclasses decide which class to instantiate; an Abstract Factory provides an interface for creating families of related objects.
Q116. What is the Builder pattern, and when is it useful?
Separates the construction of a complex object from its representation, allowing step-by-step construction (often via method chaining). Useful when a class has many optional/constructor parameters, avoiding "telescoping constructors." Common in Java via fluent builders and Lombok's @Builder.
Q117. What is the Observer pattern?
Defines a one-to-many dependency where a "subject" notifies registered "observers" of state changes automatically. Java historically provided java.util.Observer/Observable (now deprecated); modern implementations often use listener interfaces or reactive streams.
Q118. What is Dependency Injection (DI), and how does it relate to the Strategy pattern / Inversion of Control? DI is a technique where an object's dependencies are provided (injected) externally rather than created internally, promoting loose coupling and testability. It's an implementation of the broader Inversion of Control (IoC) principle. Frameworks like Spring manage object lifecycles and wire dependencies automatically (via constructor, setter, or field injection).
Q119. What is the Decorator pattern, and where is it used in the JDK?
Attaches additional responsibilities to an object dynamically by wrapping it in another object implementing the same interface, without altering the original class. Classic JDK example: java.io stream wrapping — new BufferedReader(new InputStreamReader(new FileInputStream("f.txt"))).