Java Design Patterns Interview Questions 2026: 50+ Q&A for FAANG & Senior Engineers
Design patterns are among the most heavily tested topics in senior Java engineer interviews at FAANG, tier-1 startups, and large enterprises. This guide covers 50+ carefully curated Q&A — from GoF fundamentals through creational, structural, and behavioral patterns — with real Spring Boot code examples, trade-off tables, and the kind of nuanced answers that separate senior from junior engineers.
TL;DR — What Interviewers Really Test
"Interviewers don't just ask you to name patterns — they test whether you know why to use them, when not to, and how they manifest in real frameworks like Spring Boot. Master the intent, trade-offs, and real-world examples for all 23 GoF patterns, and you'll be prepared for any seniority level."
Table of Contents
1. Fundamentals — What Are Design Patterns?
Q1: What are design patterns and why should every Java developer know them?
Answer: Design patterns are reusable solutions to commonly occurring problems in software design. They were popularized by the "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) in their 1994 book Design Patterns: Elements of Reusable Object-Oriented Software. Patterns are not copy-paste code snippets — they are templates describing how to structure objects and classes to solve a specific design problem in a given context.
Every Java developer should know them because: (1) they give a shared vocabulary so teams can communicate design intent concisely ("use a Strategy here"), (2) they encode decades of design knowledge so you don't reinvent the wheel, and (3) major frameworks like Spring, Hibernate, and JDK itself are built on these patterns.
Q2: What are the 3 categories of GoF patterns? Name all 23.
Answer: The 23 GoF patterns are grouped into three categories:
| Creational (5) | Structural (7) | Behavioral (11) |
|---|---|---|
| Singleton | Adapter | Chain of Responsibility |
| Factory Method | Bridge | Command |
| Abstract Factory | Composite | Interpreter |
| Builder | Decorator | Iterator |
| Prototype | Facade | Mediator |
| Flyweight | Memento | |
| Proxy | Observer | |
| State | ||
| Strategy | ||
| Template Method | ||
| Visitor |
Q3: What is the difference between a design pattern and an algorithm?
Answer: An algorithm is a precise step-by-step computational procedure with a defined input and output (e.g., QuickSort). A design pattern describes the structure and relationships between objects and classes at a higher level of abstraction, without specifying exact code. Two implementations of the same pattern can look very different while solving the same structural problem.
Q4: What are anti-patterns and how are they different from design patterns?
Answer: Anti-patterns are common responses to recurring problems that appear reasonable but produce worse outcomes than alternatives. Examples: God Object (one class does too much), Singleton overuse (hidden global state), and Golden Hammer (using the same pattern for every problem). Design patterns solve problems well; anti-patterns appear to solve them but create new issues.
Q5: When should you NOT use a design pattern?
Answer: Don't use a pattern when: (1) the problem is simple and a direct solution is more readable, (2) you're adding abstraction for hypothetical future requirements (YAGNI), (3) the pattern increases complexity without providing flexibility you actually need, or (4) the team is unfamiliar with the pattern and it will hurt maintainability. Patterns should reduce complexity in context — not signal design sophistication.
2. Creational Patterns — 12 Interview Questions
Q6: Implement a thread-safe Singleton in Java. What are the approaches?
Answer: There are three main approaches in modern Java:
// Approach 1: Eager initialization (simplest, thread-safe)
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() { return INSTANCE; }
}
// Approach 2: Double-checked locking (lazy, thread-safe)
public class LazyDCL {
private static volatile LazyDCL instance;
private LazyDCL() {}
public static LazyDCL getInstance() {
if (instance == null) {
synchronized (LazyDCL.class) {
if (instance == null) {
instance = new LazyDCL();
}
}
}
return instance;
}
}
// Approach 3: Enum Singleton (best — serialization-safe, reflection-safe)
public enum EnumSingleton {
INSTANCE;
public void doWork() { /* ... */ }
}
The enum approach is recommended by Effective Java (Item 3) — it handles serialization and reflection attacks automatically. The volatile keyword in double-checked locking prevents partial object visibility across CPU cores.
Q7: What is the difference between Factory Method and Abstract Factory?
Answer:
| Aspect | Factory Method | Abstract Factory |
|---|---|---|
| Creates | One product | Families of related products |
| Mechanism | Subclassing (override method) | Composition (inject factory) |
| Use when | One type needs flexible creation | Switch entire product families |
| Spring example | @Bean factory methods | DataSource provider families |
Q8: When should you use Builder over a constructor or factory?
Answer: Use Builder when: (1) an object has many optional parameters (telescoping constructor problem), (2) you need immutable objects with complex initialization, (3) the object requires validation before construction. Lombok's @Builder generates Builder boilerplate automatically in Spring Boot.
// Builder pattern — fluent, immutable, readable
@Builder
@Value // Lombok: immutable + all-args constructor
public class CreateOrderRequest {
String customerId;
String productId;
int quantity;
String shippingAddress;
String couponCode; // optional
PaymentMethod paymentMethod;
}
// Usage — no telescoping constructor, self-documenting
CreateOrderRequest request = CreateOrderRequest.builder()
.customerId("C-123")
.productId("P-456")
.quantity(2)
.shippingAddress("123 Main St")
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
Q9: What is the Prototype pattern and when do you use it in Java?
Answer: Prototype creates new objects by cloning existing ones rather than constructing from scratch. Use it when: (1) object construction is expensive (e.g., pre-built complex objects), (2) objects differ only slightly from each other, (3) you want to avoid subclassing just for configuration. In Java, implement Cloneable or use copy constructors. Important: distinguish shallow clone (reference copy) from deep clone (full recursive copy).
Q10: How does Spring Bean scope relate to the Singleton pattern?
Answer: Spring's default @Scope("singleton") creates one bean instance per ApplicationContext (not per JVM classloader like GoF Singleton). This is safer: Spring manages lifecycle, no static state, easy to test with different contexts. @Scope("prototype") creates a new instance per injection point — analogous to the Prototype pattern.
Q11: How does Spring @Configuration + @Bean implement Factory Method pattern?
Answer: In Spring, @Configuration classes act as ConcreteCreator and @Bean methods are the factory methods that create and configure product instances. Different profiles can have different @Configuration classes providing different implementations — this is exactly the Factory Method intent: deferring instantiation to subclasses (profiles/environments).
Q12: What are the risks of overusing Singleton in a Spring Boot application?
Answer: Risks include: (1) Mutable shared state — instance variables in singleton beans are shared across all requests/threads, causing race conditions, (2) Hidden dependencies — static access makes dependencies opaque and untestable, (3) Testing difficulty — hard to mock without dependency injection. Solution: keep singleton beans stateless, use method-local variables, and inject all dependencies via constructor.
3. Structural Patterns — 13 Interview Questions
Q13: What is the difference between Adapter and Facade?
Answer: Adapter converts an incompatible interface into the one a client expects — it bridges a mismatch (e.g., adapting a legacy payment API to a new PaymentGateway interface). Facade provides a simplified interface over a complex subsystem — it hides complexity, not incompatibility (e.g., OrderFacade that hides calls to InventoryService, PaymentService, and NotificationService). Adapter is about interface conversion; Facade is about simplification.
Q14: Decorator vs Inheritance — when to use Decorator to extend behavior?
Answer: Use Decorator when you need to add responsibilities dynamically at runtime or combine behaviors in multiple ways without a class explosion. Example: OrderService → LoggingOrderService(OrderService) → MetricsOrderService(LoggingOrderService). Each wrapper adds behavior and delegates to the wrapped object. Inheritance requires a new subclass for every combination of behaviors (combinatorial explosion).
// Decorator pattern — adds behavior without subclassing
public interface OrderService {
Order createOrder(CreateOrderRequest request);
}
public class LoggingOrderServiceDecorator implements OrderService {
private final OrderService delegate;
public LoggingOrderServiceDecorator(OrderService delegate) {
this.delegate = delegate;
}
@Override
public Order createOrder(CreateOrderRequest request) {
log.info("Creating order for customer {}", request.getCustomerId());
Order order = delegate.createOrder(request);
log.info("Order created: {}", order.getId());
return order;
}
}
Q15: What are the four types of Proxy and when does each apply?
| Proxy Type | Purpose | Java / Spring Example |
|---|---|---|
| Virtual | Lazy initialization of expensive objects | Hibernate lazy-loaded associations |
| Remote | Hide network call behind local interface | Spring Cloud OpenFeign client |
| Protection | Control access based on permissions | Spring Security @PreAuthorize proxy |
| Smart Reference | Add cross-cutting concerns transparently | Spring AOP @Transactional, @Cacheable |
Q16: How does Spring AOP use the Proxy pattern?
Answer: Spring AOP wraps Spring beans in JDK dynamic proxies (interface-based) or CGLIB proxies (class-based). When you annotate a method with @Transactional or @Cacheable, Spring does not modify your class. Instead it creates a proxy object that intercepts method calls, runs the cross-cutting logic (begin transaction, check cache), delegates to the real object, then runs post-processing. This is the Smart Reference Proxy pattern at the framework level.
Q17: What is the Composite pattern and when do you use it?
Answer: Composite lets you treat individual objects and compositions of objects uniformly through a shared interface. Use it for tree structures where leaf and branch nodes need to be interchangeable. Examples: UI component trees, permission groups containing individual permissions, a MenuItem interface implemented by both leaf ActionItem and MenuGroup (which contains more MenuItems).
Q18: What is the Flyweight pattern? Give a Java example.
Answer: Flyweight shares object instances that have identical intrinsic state to reduce memory usage. The canonical Java example is String.intern() and the String pool — identical string literals are shared rather than duplicated. Another example: Java's Integer.valueOf() caches values between -128 and 127. Use Flyweight when creating massive numbers of fine-grained objects (e.g., characters in a text editor, particles in a game).
Q19: Is a Service Layer the same as a Facade? Explain the relationship.
Answer: A Service Layer (Martin Fowler's pattern) often implements the Facade pattern — it provides a simple interface over complex domain operations. But they're not identical: (1) a Facade's primary goal is simplifying an interface, while a Service Layer's goal is defining the application's use cases and transaction boundaries; (2) Service Layers also enforce security, apply business rules, and coordinate multiple domain objects. In practice, Spring @Service classes act as both Facade and Transaction coordinator.
4. Behavioral Patterns — 15 Interview Questions
Q20: Strategy vs State — what is the key difference?
Answer: Both encapsulate behavior in interchangeable objects, but: Strategy — the algorithm choice is determined externally (the client selects which strategy to use; strategies don't know about each other). State — the object transitions between states internally (states know about transitions to other states; the context doesn't manage transitions directly). Example: SortingStrategy is selected by the client (Strategy). OrderState transitions from PENDING → PAID → SHIPPED internally (State).
Q21: How do you implement Strategy pattern to replace a complex if-else chain?
// Before: fragile switch — adding payment type requires modifying this class
public class PaymentProcessor {
public void process(Order order, String type) {
if (type.equals("STRIPE")) { /* stripe logic */ }
else if (type.equals("PAYPAL")) { /* paypal logic */ }
else if (type.equals("CRYPTO")) { /* crypto logic */ }
}
}
// After: Strategy pattern — open/closed, easily extensible
public interface PaymentStrategy {
void process(Order order);
}
@Component("STRIPE")
public class StripePaymentStrategy implements PaymentStrategy {
public void process(Order order) { /* stripe-specific logic */ }
}
@Component("PAYPAL")
public class PaypalPaymentStrategy implements PaymentStrategy {
public void process(Order order) { /* paypal-specific logic */ }
}
// Spring auto-injects all PaymentStrategy beans by name
@Service
public class PaymentProcessor {
private final Map<String, PaymentStrategy> strategies;
public PaymentProcessor(Map<String, PaymentStrategy> strategies) {
this.strategies = strategies;
}
public void process(Order order, String type) {
strategies.getOrDefault(type, unsupportedStrategy()).process(order);
}
}
Q22: Observer vs Event-Driven Architecture — how does Kafka relate?
Answer: Observer (GoF) is an in-process synchronous pattern: subject notifies registered observers directly within the same JVM. Event-driven architecture (with Kafka/RabbitMQ) is the distributed, asynchronous equivalent: producers publish events to a broker; consumers subscribe independently. Key differences: (1) Observer is in-memory and synchronous; EDA is network-based and async; (2) EDA survives process crashes via broker persistence; (3) EDA decouples producers and consumers completely. Spring's @EventListener is an in-process observer; Spring Kafka is distributed event-driven.
Q23: Implement Command pattern for order processing with undo support.
// Command interface with undo
public interface OrderCommand {
void execute();
void undo();
}
public class PlaceOrderCommand implements OrderCommand {
private final OrderRepository repo;
private final Order order;
public PlaceOrderCommand(OrderRepository repo, Order order) {
this.repo = repo; this.order = order;
}
@Override public void execute() { repo.save(order); }
@Override public void undo() { repo.delete(order.getId()); }
}
// CommandInvoker maintains history for undo
public class OrderCommandInvoker {
private final Deque<OrderCommand> history = new ArrayDeque<>();
public void execute(OrderCommand cmd) {
cmd.execute();
history.push(cmd);
}
public void undoLast() {
if (!history.isEmpty()) history.pop().undo();
}
}
Q24: Template Method vs Strategy — when do you choose each?
Answer: Template Method uses inheritance: the base class defines the skeleton (fixed steps), and subclasses override specific steps. It's a compile-time choice. Strategy uses composition: the algorithm is fully replaceable at runtime by injecting different strategy objects. Choose Template Method for frameworks where the overall flow is fixed but specific steps vary (Spring's AbstractController, JdbcTemplate). Choose Strategy when the entire algorithm varies and you want runtime flexibility without subclassing.
Q25: How does Spring Security's filter chain implement Chain of Responsibility?
Answer: Spring Security's FilterChainProxy maintains an ordered list of SecurityFilter instances. Each filter handles its concern (authentication, CORS, CSRF, authorization) and passes the request to the next filter via chain.doFilter(). This is exactly Chain of Responsibility: each handler decides whether to process the request or pass it along. You can insert custom filters at any point in the chain using addFilterBefore() or addFilterAfter().
Q26: What is the Mediator pattern and when is it better than Observer?
Answer: Mediator centralizes communication between objects: instead of objects referencing each other directly, they communicate through a mediator. Observer broadcasts to all subscribers (1-to-many). Use Mediator when: (1) many-to-many interactions between objects are getting complex, (2) you need controlled, orchestrated interactions (not just broadcasts), (3) you want to extract the interaction logic into one place. Spring's ApplicationEventPublisher acts as a mediator/event bus.
Q27: What is the Memento pattern? Implement an order draft with undo.
Answer: Memento captures and externalizes an object's state without violating encapsulation, so the object can be restored to that state later. Use it for undo/redo and snapshot functionality.
// Memento holds snapshot of OrderDraft state
public record OrderDraftMemento(String productId, int qty, String coupon) {}
public class OrderDraft {
private String productId;
private int quantity;
private String couponCode;
public OrderDraftMemento save() {
return new OrderDraftMemento(productId, quantity, couponCode);
}
public void restore(OrderDraftMemento memento) {
this.productId = memento.productId();
this.quantity = memento.qty();
this.couponCode = memento.coupon();
}
// setters...
}
Q28: What is the Visitor pattern and when would you use double dispatch?
Answer: Visitor lets you add new operations to an object structure without modifying existing classes. It uses double dispatch: the object calls accept(visitor), which calls visitor.visit(this) — the correct method is selected based on both the element type and visitor type. Use Visitor when: (1) you have a stable object hierarchy but need to add many operations, (2) you want to separate concerns (the element classes shouldn't know about reporting/export logic). Example: OrderItem.accept(TaxCalculatorVisitor).
5. Advanced & Architectural Questions
Q29: How would you select a pattern for a payment processing system?
Answer: Think through the requirements: (1) Multiple payment providers (Stripe, PayPal, Crypto) → Strategy pattern for interchangeable algorithms; (2) Payment provider switch without code changes → Abstract Factory for provider families; (3) Cross-cutting concerns (logging, retry, metrics) → Decorator or AOP Proxy; (4) Complex payment flow (validate → charge → notify → record) → Template Method or Chain of Responsibility; (5) Undo/reversal → Command pattern. Real systems combine multiple patterns — Strategy for provider selection + Decorator for logging + Command for reversibility.
Q30: Are microservices patterns (Saga, Outbox, CQRS) GoF patterns?
Answer: No — Saga, Outbox, CQRS, and Strangler Fig are distributed systems patterns, not GoF patterns. GoF patterns operate at the object/class level within a single process. Microservices patterns operate at the service/network level. However, GoF patterns appear inside microservices: Saga orchestration uses Command + Mediator; CQRS separates command from query objects (like Command pattern); Outbox uses Observer (database events trigger outbox listener).
Q31: How do you combine Strategy + Factory + Template Method?
Answer: A common combination for report generation: (1) Template Method in abstract ReportGenerator defines the steps (fetch data → format → render → deliver); (2) Strategy for the formatter (CSV, PDF, Excel) selected at runtime; (3) Factory Method in ReportGeneratorFactory.create(type) returning the appropriate concrete generator. Each pattern handles a different variation axis: the overall flow (Template Method), the output format (Strategy), and which generator to instantiate (Factory Method).
Q32: What is the difference between design patterns and SOLID principles?
Answer: SOLID principles are guidelines for what good design looks like (high-level heuristics). Design patterns are concrete solutions to specific recurring problems. Patterns implement principles: Strategy pattern implements OCP (Open/Closed Principle — open for extension); Observer implements DIP (depend on abstractions, not concretions); Dependency injection implements DIP directly. Principles tell you what to aim for; patterns show you proven ways to get there.
Q33: How do you identify design patterns in an existing codebase?
Answer: Look for structural signatures: (1) multiple classes implementing the same interface with a context class that holds one → Strategy; (2) class wrapping another instance of the same interface → Decorator or Proxy; (3) class with a create() or build() method → Factory or Builder; (4) event listeners / notify() calls → Observer; (5) abstract class with templateMethod() calling abstract steps → Template Method. IDE tools (IntelliJ's UML diagram) and SonarQube structural rules help identify patterns programmatically.
6. Spring Boot Specific Questions
Q34: Which design patterns does Spring Boot use internally? Give examples.
| Pattern | Spring Feature | Where |
|---|---|---|
| Singleton | Default bean scope | ApplicationContext bean management |
| Factory Method | @Bean methods in @Configuration | BeanFactory, ApplicationContext |
| Proxy | @Transactional, @Cacheable, @Async | Spring AOP (JDK + CGLIB proxies) |
| Template Method | JdbcTemplate, RestTemplate | Spring JDBC, WebMVC |
| Observer | @EventListener, ApplicationEvent | Spring Events framework |
| Decorator | BeanPostProcessor, BeanWrapper | ApplicationContext lifecycle |
| Strategy | HttpMessageConverter selection | DispatcherServlet content negotiation |
Q35: How do you implement the Observer pattern with Spring Events?
// 1. Define the event (replaces ConcreteEvent)
public class OrderPlacedEvent {
private final String orderId;
public OrderPlacedEvent(String orderId) { this.orderId = orderId; }
public String getOrderId() { return orderId; }
}
// 2. Publisher (replaces Subject)
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public Order placeOrder(CreateOrderRequest request) {
Order order = /* create order */ null;
eventPublisher.publishEvent(new OrderPlacedEvent(order.getId()));
return order;
}
}
// 3. Observer — decoupled, no direct dependency on OrderService
@Component
public class InventoryReservationListener {
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
// reserve inventory for order
}
}
@Component
public class EmailNotificationListener {
@EventListener
@Async // non-blocking observer
public void onOrderPlaced(OrderPlacedEvent event) {
// send confirmation email
}
}
Q36: How do you implement a Repository pattern in Spring Data?
Answer: Spring Data JPA's JpaRepository is the Repository pattern: it provides a collection-like interface for accessing domain objects, hiding all JPA/SQL details. Extending JpaRepository<Order, String> gives you CRUD operations, pagination, and query derivation from method names. The interface is a Pure Fabrication (GRASP) — it doesn't exist in the domain but provides clean separation between domain and persistence. Custom queries use @Query, native SQL, or Specifications.
Q37: How does @Transactional relate to the Proxy and Decorator patterns?
Answer: Spring wraps your @Transactional service in a CGLIB proxy. The proxy intercepts method invocations, begins a transaction, calls the real method, then commits or rolls back. This is the Smart Reference Proxy pattern — same interface, transparent to the caller, extra behavior added around the real object. It's also similar to Decorator (adding transaction behavior) but differs in that Proxy controls access/lifecycle rather than composing behaviors. Key gotcha: calling a @Transactional method from within the same class bypasses the proxy.
Q38: When would you use @Scope("prototype") and how does it relate to Prototype pattern?
Answer: Use @Scope("prototype") when a bean holds stateful, request-specific data (e.g., a shopping cart, session object). Spring creates a new instance for each injection point, analogous to the Prototype pattern's clone-to-create approach. Important: injecting a prototype bean into a singleton bean means the singleton always gets the same prototype instance — use ApplicationContext.getBean() or a @Lookup method to get fresh instances each time.
7. Quick-Reference Cheat Sheet
Use this table as a last-minute review before your interview:
| Pattern | Category | Use When | Spring Example |
|---|---|---|---|
| Singleton | Creational | One instance per app | Default @Component scope |
| Factory Method | Creational | Defer instantiation to subclass | @Bean in @Configuration |
| Abstract Factory | Creational | Switch product families | @Profile + @Configuration |
| Builder | Creational | Many optional parameters | Lombok @Builder, WebClient |
| Prototype | Creational | Clone expensive objects | @Scope("prototype") |
| Adapter | Structural | Convert incompatible interface | JPA to domain adapter |
| Decorator | Structural | Add behavior dynamically | BeanPostProcessor |
| Proxy | Structural | Control access, add cross-cutting | @Transactional, @Cacheable |
| Facade | Structural | Simplify complex subsystem | @Service as use-case facade |
| Composite | Structural | Tree structures, part-whole | Security role hierarchies |
| Flyweight | Structural | Share identical fine-grained objects | String pool, Integer cache |
| Strategy | Behavioral | Swappable algorithms at runtime | Map<String, Strategy> injection |
| Observer | Behavioral | Notify dependents of state change | @EventListener |
| Command | Behavioral | Encapsulate request, undo/redo | Spring Batch ItemReader |
| Chain of Resp. | Behavioral | Pass request through handler chain | Spring Security FilterChain |
| Template Method | Behavioral | Fixed skeleton, vary steps | JdbcTemplate, RestTemplate |
| State | Behavioral | Behavior changes with state | Order lifecycle (PENDING→PAID) |
| Mediator | Behavioral | Centralize many-to-many comm. | ApplicationEventPublisher |
| Memento | Behavioral | Undo/redo, snapshot | Hibernate dirty checking |
| Iterator | Behavioral | Traverse without exposing structure | Java for-each / Iterable |
| Visitor | Behavioral | Add operations without modifying | AST processing, tax calculators |
| Interpreter | Behavioral | Parse domain-specific language | Spring Expression Language (SpEL) |
Conclusion & Interview Checklist
Before your next senior Java engineer interview, make sure you can:
- Name and categorize all 23 GoF patterns without hesitation
- Explain the intent of each pattern — not just the structure
- Give at least one Spring Boot example for each pattern you're asked about
- Articulate when NOT to use a pattern (YAGNI, overengineering)
- Implement Singleton (enum), Builder, Strategy, Observer, Command, and Proxy from memory
- Distinguish Strategy vs State, Adapter vs Facade, Decorator vs Proxy
- Connect patterns to SOLID principles and explain how each pattern implements which principle
- Discuss how Spring AOP proxies work under the hood
The engineers who stand out don't just recite pattern definitions — they tell a story about why they chose a pattern in a real project, what the trade-offs were, and what they would do differently. Study these patterns in the context of systems you've actually built, and you'll have compelling answers ready for any depth of questioning.