Java / J2EE Design Patterns with Java 21 Interview questions
Last updated
1. What is a design pattern in J2EE?
A design pattern in J2EE is a proven, named solution to a problem that keeps showing up when you build multi-tier enterprise applications. It is not a library or a class you import. It is a template for how to arrange classes and responsibilities across the web, business and data tiers.
The best-known catalog is Core J2EE Patterns by Alur, Crupi and Malks. It describes patterns such as Front Controller, Session Facade and Data Access Object. The platform has since been renamed Jakarta EE, but the underlying problems (request handling, remote calls, persistence) are the same.
Patterns give teams a shared vocabulary. Saying "put a Session Facade in front of those services" is faster than describing the whole structure.
Take quiz
a Maven plugin that generates boilerplate
a reusable, named solution to a recurring design problem
a vendor-specific application server feature
a JDBC driver optimisation
the JDK 21 release notes
the Servlet 6.0 specification only
the JPA reference implementation
Core J2EE Patterns by Alur, Crupi and Malks
2. What are the categories of J2EE design patterns?
Core J2EE patterns are grouped by the tier they belong to: presentation, business and integration.
Presentation-tier patterns deal with web requests and views. Business-tier patterns organise services and domain logic. Integration-tier patterns hide how the application talks to databases, messaging systems and external services.
| Tier | Typical patterns |
| Presentation | Intercepting Filter, Front Controller, View Helper, Composite View |
| Business | Business Delegate, Session Facade, Service Locator, Transfer Object |
| Integration | Data Access Object, Service Activator, Web Service Broker |
Many teams also use the general GoF patterns (Singleton, Factory, Strategy) inside each tier.
Take quiz
presentation tier
client tier
integration tier
none, it is a GoF-only pattern
presentation tier
business tier
integration tier
resource tier
3. What is the MVC pattern?
MVC splits a web application into three roles: Model (data and business state), View (what the user sees) and Controller (handles input and decides what happens next).
In a Jakarta EE app, a servlet or a framework controller receives the request, calls a service to update the model, and then forwards to a view such as a JSP, Facelets page or JSON serializer.
The main benefit is that the view can change without touching business rules, and business rules can be tested without a browser.
Take quiz
Model
View
Data source
Controller
it removes the need for a database
separation of UI, input handling and business state
it makes every request run in a single thread
it replaces dependency injection
4. What is the Front Controller pattern?
Front Controller routes every incoming request through one central handler instead of letting each page process its own request. That handler does common work (authentication, logging, locale) and then dispatches to the right command or controller.
Spring MVC's DispatcherServlet, Jakarta Faces' FacesServlet and Struts' ActionServlet are all front controllers.
@WebServlet("/app/*") public class FrontServlet extends HttpServlet { private final Map<String, Command> routes = Map.of( "/orders", new OrderCommand(), "/users", new UserCommand()); @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws IOException { Command c = routes.get(req.getPathInfo()); if (c == null) { res.sendError(404); return; } c.execute(req, res); } }
Take quiz
handle all requests centrally and dispatch them
cache database rows
generate SQL
manage JVM garbage collection
java.util.Timer
javax.sql.DataSource
Spring's DispatcherServlet
java.lang.ThreadLocal
5. What is the Singleton pattern?
Singleton ensures a class has exactly one instance per JVM (strictly, per class loader) and gives a global access point to it. Typical uses are configuration holders, registries and caches.
In Java 21 the safest plain-Java version is a single-element enum, because the JVM guarantees one instance even with serialization and reflection attacks.
public enum AppConfig { INSTANCE; private final Properties props = load(); public String get(String key) { return props.getProperty(key); } private static Properties load() { /* read file */ return new Properties(); } }
Take quiz
a public constructor with a static counter
a single-element enum
a non-final static field set by any caller
cloning a prototype object
server cluster
database
HTTP session
class loader in a JVM
6. What is the Factory Method pattern?
Factory Method defines an interface for creating an object but lets subclasses or a dedicated method decide which concrete class to instantiate. The caller depends on the abstraction, not on new ConcreteClass().
A common J2EE example is a DaoFactory that returns a MySQL or Oracle implementation of the same DAO interface, based on configuration.
interface Notifier { void send(String msg); } class EmailNotifier implements Notifier { public void send(String m) { /* smtp */ } } class SmsNotifier implements Notifier { public void send(String m) { /* sms */ } } class NotifierFactory { static Notifier of(String type) { return switch (type) { case "email" -> new EmailNotifier(); case "sms" -> new SmsNotifier(); default -> throw new IllegalArgumentException(type); }; } }
Take quiz
the HTTP status code
the thread priority
which concrete class gets instantiated
the database schema
you can swap implementations without changing callers
it makes SQL run faster
it removes the need for interfaces
it enables multiple inheritance
7. What is the DAO pattern?
The Data Access Object pattern puts all database access for one entity behind an interface. Business code calls userDao.findById(7) and never sees JDBC, SQL or JPA details.
This keeps persistence swappable (JDBC today, JPA tomorrow) and makes services easy to unit test by mocking the DAO.
public interface UserDao { Optional<User> findById(long id); List<User> findByCity(String city); void save(User user); void delete(long id); }
Take quiz
HTTP routing
UI rendering
thread scheduling
persistence logic for an entity
tests no longer need assertions
services can be tested with a mocked DAO
it removes the need for a build tool
it guarantees faster SQL
8. What is a Data Transfer Object?
A Data Transfer Object (called Transfer Object in the Core J2EE catalog) is a simple carrier of data between layers or across a network call. It holds fields and nothing else, so one call can move several values at once instead of many chatty getter calls.
In Java 21 a record is the natural way to write one: immutable, concise and with equals, hashCode and toString generated.
public record UserDto(long id, String name, String email) {}
Take quiz
data fields only, no business logic
database connections
servlet filters
transaction managers
enum
abstract class
record
synchronized block
9. What is the Business Delegate pattern?
Business Delegate is a client-side object that stands between the presentation tier and the business services. The web layer calls the delegate, and the delegate handles lookup, retries and translating remote exceptions into application exceptions.
The point was to shield UI code from the details of EJB remote calls. If the service API changes, only the delegate changes.
Take quiz
it sits between the database and the disk
it sits between presentation and business tiers
it replaces the servlet container
it lives only inside the JVM garbage collector
JVM start-up time
network latency to zero
the number of database tables
coupling between the UI and remote service details
10. What is the Service Locator pattern?
Service Locator is a central class that looks up services (JNDI resources, EJBs, data sources) by name and caches them. Clients ask the locator instead of doing their own JNDI lookups, which are costly and repetitive.
Today dependency injection (CDI @Inject, Spring) does the same job without a global lookup class, so Service Locator is mostly seen in legacy code.
public final class ServiceLocator { private static final Map<String, Object> cache = new ConcurrentHashMap<>(); public static <T> T lookup(String jndiName, Class<T> type) { return type.cast(cache.computeIfAbsent(jndiName, n -> { try { return new InitialContext().lookup(n); } catch (NamingException e) { throw new IllegalStateException(e); } })); } }
Take quiz
HTTP sessions
SQL result sets only
results of JNDI lookups
compiled JSPs
dependency injection
static global variables
bigger heap sizes
XML-only configuration
11. What is the Session Facade pattern?
Session Facade puts a coarse-grained service object in front of several fine-grained business objects or entities. A client makes one call, such as placeOrder(), and the facade coordinates inventory, payment and shipping inside a single transaction.
In classic J2EE the facade was a stateless session bean. In Jakarta EE it is usually an @Stateless bean or a Spring @Service with @Transactional.
@Stateless public class OrderFacade { @Inject InventoryService inventory; @Inject PaymentService payment; @Inject ShippingService shipping; public Receipt placeOrder(OrderRequest r) { inventory.reserve(r.items()); var receipt = payment.charge(r.card(), r.total()); shipping.schedule(r.address(), r.items()); return receipt; } }
Take quiz
to avoid using transactions
to store HTTP cookies
to compile JSP files
to expose one coarse-grained call over many fine-grained services
inside each JSP
at the facade method boundary
in the browser
in the DAO's constructor
12. What is the Intercepting Filter pattern?
Intercepting Filter lets you run pre- and post-processing around a request without changing the target resource. You chain filters, each one doing a single job: authentication, logging, compression, input sanitising.
In Jakarta EE this is implemented with jakarta.servlet.Filter. Each filter calls chain.doFilter() to pass control onward.
@WebFilter("/*") public class TimingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { long t = System.nanoTime(); chain.doFilter(req, res); System.out.println("took " + (System.nanoTime() - t) / 1_000_000 + " ms"); } }
Take quiz
chain.doFilter(request, response)
response.flushBuffer()
request.getSession()
filter.destroy()
generating database primary keys
laying out HTML tables
cross-cutting concerns like logging and authentication
compiling Java sources
13. What is the View Helper pattern?
View Helper moves processing logic out of the view and into helper classes so the page only does presentation. A JSP full of scriptlets is hard to read and test. With View Helper, the page calls a tag, a bean or an EL expression and the helper does the work.
JSTL tags, custom tag libraries and Facelets backing beans are all view helpers.
Take quiz
slow database indexes
logic mixed into the view
missing HTTP headers
unsafe class loading
a JDBC PreparedStatement
a ThreadPoolExecutor
a JNDI context
a custom JSP tag or JSTL tag
14. What is the Composite View pattern?
Composite View builds a page from smaller, independent sub-views such as header, menu, content and footer. Each piece can be reused and changed separately, and the layout is assembled at render time.
Apache Tiles, Facelets templates (ui:composition, ui:insert) and JSP includes implement this idea.
Take quiz
database rows only
multiple JVMs
reusable sub-views
compiled servlets only
Facelets
JDBC
JMS
JNDI
15. What is the Value List Handler pattern?
Value List Handler manages large query results on the server and hands the client one page at a time. It keeps a cursor or the query state, so the UI does not pull ten thousand rows into memory.
Today you would usually do this with setFirstResult() and setMaxResults() in JPA, or Spring Data's Pageable.
Take quiz
encrypting passwords
routing JMS messages
starting servlets at boot
paging through large result sets
Runnable
Pageable
Serializable
Cloneable
16. What is the Observer pattern?
Observer defines a one-to-many link: when a subject changes state, all registered observers are notified automatically. The subject does not know the concrete observers, which keeps them loosely coupled.
Java's old java.util.Observable is deprecated. Use listeners, java.util.concurrent.Flow or CDI events instead.
interface PriceListener { void onPrice(String symbol, double price); } class PriceFeed { private final List<PriceListener> listeners = new CopyOnWriteArrayList<>(); void subscribe(PriceListener l) { listeners.add(l); } void publish(String s, double p) { listeners.forEach(l -> l.onPrice(s, p)); } }
Take quiz
the subject, when its state changes
the garbage collector
the compiler
the web browser
required by Jakarta EE
the only way to build listeners
deprecated
a Java 21 addition
17. What is the Builder pattern?
Builder constructs a complex object step by step, so you avoid constructors with a dozen parameters. You set only what you need, and build() returns the finished (usually immutable) object.
It also reads well: you see names next to values, which a call like new Order(1, 2, true, null, 5) never gives you.
Order order = Order.builder() .customerId(42) .item("SKU-9", 2) .expressShipping(true) .build();
Take quiz
thread starvation
unwieldy constructors with many optional parameters
slow garbage collection
JNDI lookups
a database connection
the builder itself, unchanged
a new thread
the completed, often immutable object
18. What is the Strategy pattern?
Strategy puts each algorithm behind a common interface so you can pick one at runtime. The client holds a reference to the interface and does not care which implementation runs.
A typical case is shipping cost: flat rate, weight based, or free over a threshold. Instead of a growing if-else block, each rule is a class (or a lambda).
interface ShippingStrategy { double cost(Order o); } ShippingStrategy flat = o -> 5.0; ShippingStrategy weight = o -> o.weightKg() * 1.2; ShippingStrategy chosen = o.total() > 100 ? (x -> 0.0) : weight; double fee = chosen.cost(o);
Take quiz
share one instance across all JVMs
avoid writing interfaces
swap algorithms at runtime behind one interface
replace the database
large if-else or switch blocks choosing behaviour
JDBC drivers
web.xml files
thread pools
19. Describe the Proxy pattern?
A Proxy is a stand-in object with the same interface as the real one. It controls access and can add behaviour such as lazy loading, security checks, caching or remote communication before delegating.
Hibernate returns proxies for lazy associations, and Spring wraps beans in proxies to apply @Transactional. In plain Java you can build one with java.lang.reflect.Proxy.
Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class<?>[]{Service.class}, (obj, m, args) -> { System.out.println("before " + m.getName()); return m.invoke(real, args); });
Take quiz
the same JVM heap address
the same class file name only
the same database row
the same interface
compiling HQL
lazy loading of associations
opening sockets
signing JARs
20. What is the Template Method pattern?
Template Method fixes the skeleton of an algorithm in a base class and leaves some steps for subclasses to fill in. The order of steps is controlled by the parent and cannot be changed by the child.
A report exporter is a good example: open, fetch data, format, close. Only format() differs between CSV and PDF.
abstract class Exporter { public final void export() { open(); var data = fetch(); format(data); close(); } protected abstract void format(List<Row> data); void open() {} List<Row> fetch() { return List.of(); } void close() {} }
Take quiz
the sequence of steps
the subclass names
the database vendor
the JVM version
to make it run in parallel
to allow serialization
to stop subclasses from changing the step order
to hide it from the compiler
21. How do records simplify DTOs in Java 21?
Records remove the boilerplate you used to write for a DTO: private final fields, constructor, getters, equals, hashCode and toString. One line gives you all of it, and the fields are immutable by default.
You can still validate input with a compact constructor, and records can implement interfaces. They cannot extend another class and they cannot be JPA entities, because JPA needs a no-arg constructor and mutable state. Use them for projections, request/response bodies and events.
public record TransferRequest(String from, String to, BigDecimal amount) { public TransferRequest { Objects.requireNonNull(from); if (amount.signum() <= 0) throw new IllegalArgumentException("amount must be positive"); } }
Take quiz
as a REST response body
as a standard JPA entity
as a Kafka event payload
as a query projection
extending another class
making fields mutable
declaring static methods only
validating or normalising components
22. How do sealed interfaces improve Strategy and Command?
A sealed interface lists exactly which classes may implement it. With Strategy or Command that gives you a closed set of variants, and the compiler knows all of them.
Combined with pattern matching for switch (final in Java 21), you get exhaustive handling without a default branch. If you add a new command, every switch that misses it stops compiling, which is a bug caught early.
sealed interface PaymentCommand permits Charge, Refund, Cancel {} record Charge(String id, long cents) implements PaymentCommand {} record Refund(String id, long cents) implements PaymentCommand {} record Cancel(String id) implements PaymentCommand {} String run(PaymentCommand c) { return switch (c) { case Charge x -> "charged " + x.cents(); case Refund x -> "refunded " + x.cents(); case Cancel x -> "cancelled " + x.id(); }; }
Take quiz
automatic multithreading
faster class loading
compile-time exhaustiveness checks
reflection-free serialization
compilation fails
it silently ignores the case
it throws at class load only
it falls back to Object.toString()
23. How does pattern matching for switch replace the Visitor pattern?
Visitor exists because Java could not dispatch on the runtime type of an argument. You added accept(visitor) to every node and a visit method per type. That is a lot of ceremony.
With sealed types, records and record patterns in Java 21 you write one switch and even deconstruct the nested data in the same line. Behaviour stays in one place, and there is no accept method on the model.
sealed interface Expr permits Num, Add, Mul {} record Num(int v) implements Expr {} record Add(Expr l, Expr r) implements Expr {} record Mul(Expr l, Expr r) implements Expr {} int eval(Expr e) { return switch (e) { case Num n -> n.v(); case Add(Expr l, Expr r) -> eval(l) + eval(r); case Mul(Expr l, Expr r) -> eval(l) * eval(r); }; }
Visitor still makes sense when the type hierarchy is open and third parties add operations, or when you target older Java versions.
Take quiz
virtual threads and JNDI
string templates and CDI
Serializable and Cloneable
sealed types with pattern matching for switch and record patterns
when you need exhaustive checks
when the type hierarchy is open and not sealed
when you use records
when you target Java 21 only
24. Why is Singleton risky in clustered J2EE applications?
A Singleton is one instance per class loader per JVM. In a cluster you have many JVMs, so you actually have many "singletons". If one holds mutable state such as a counter or cache, each node sees a different copy and results drift apart.
Hot redeploys add another problem: a new class loader creates a second instance while the old one still lives, which leaks memory. Also, hidden global state makes unit tests order-dependent.
Safer choices: keep Singletons stateless, use the container's @ApplicationScoped or @Singleton bean, and put shared state in a database, Redis or a distributed cache.
| Scope | What you really get |
| Plain static Singleton | one instance per class loader |
| CDI @ApplicationScoped | one instance per application per JVM |
| Distributed cache | one logical store shared by all nodes |
Take quiz
one instance per JVM, so several copies overall
exactly one instance across all nodes
one instance per HTTP request
one instance per database row
in a static field
in a servlet instance variable
in an external store such as a database or distributed cache
in a local file on each node
25. How does dependency injection replace Service Locator?
With Service Locator, a class asks a global registry for its dependencies. With dependency injection, the container hands them in through a constructor or field. The class never knows where they came from.
That small shift matters: dependencies become visible in the constructor, tests can pass in fakes without JNDI, and there is no static call to a locator hidden inside methods.
// Locator style: hidden dependency class Billing { void run() { var gw = ServiceLocator.lookup("java:comp/env/gateway", Gateway.class); } } // DI style: explicit dependency @ApplicationScoped class Billing { private final Gateway gateway; @Inject Billing(Gateway gateway) { this.gateway = gateway; } }
Take quiz
it removes the need for interfaces
dependencies are explicit and easy to replace in tests
it works only with EJBs
it avoids all configuration
in the constructor from the container
by the compiler
from the HTTP request
inside the class by calling a global lookup
26. What is the difference between DAO and Repository?
Both hide persistence, but they speak different languages. A DAO is table-centric: its methods mirror database operations such as insert, update and findByCity. A Repository comes from Domain-Driven Design and acts like an in-memory collection of aggregate roots, using domain terms.
In practice, Spring Data's JpaRepository sits between the two. The important design call is whether callers think in rows or in domain objects.
| DAO | Repository |
| Data-centric, one per table or entity | Domain-centric, one per aggregate root |
| Methods like insert/update/delete | Methods like add/remove/findActiveCustomers |
| Core J2EE integration pattern | DDD pattern |
Take quiz
JDBC connections
servlet mappings
aggregate roots in the domain model
JNDI names
DAO
Repository
Facade
Observer
27. What is the difference between Factory Method and Abstract Factory?
Factory Method creates one product and lets a subclass or method choose the concrete type. Abstract Factory creates a family of related products that must work together, and it hides which family you are using.
Example: a UI toolkit needs a Button and a Checkbox. Abstract Factory guarantees you get both from the Dark theme or both from the Light theme, never a mix.
| Factory Method | Abstract Factory |
| Builds a single product | Builds a family of related products |
| Usually one method, often overridden | Several factory methods in one interface |
Example: NotifierFactory.of(type) |
Example: ThemeFactory with createButton() and createCheckbox() |
Take quiz
Factory Method
Singleton
Observer
Abstract Factory
a family of products
a single product type
only singletons
only proxies
28. How do virtual threads affect thread-per-request designs?
Virtual threads (final in Java 21, JEP 444) are cheap JVM-managed threads. You can start hundreds of thousands of them, so the simple model of one thread per request works again, even for blocking JDBC or HTTP calls.
That reduces the need for reactive pipelines in many Jakarta EE and Spring apps. You write plain blocking code and the JVM parks the virtual thread while it waits on I/O.
Three habits change: do not pool virtual threads, limit concurrency with a Semaphore if a downstream resource has a cap (such as a connection pool), and avoid long blocking inside synchronized blocks, which can pin the carrier thread in Java 21.
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) { for (Order o : orders) { exec.submit(() -> orderService.process(o)); } }
Take quiz
create one per task, do not pool them
pool them in a fixed-size executor
reuse one for the whole application
convert them to platform threads first
calling a record accessor
using a switch expression
blocking inside a synchronized block
reading a final field
29. How does Intercepting Filter work with Jakarta Servlet filters?
The container builds a FilterChain for each request from the filters whose URL patterns match. The request enters filter 1, which does its pre-work and calls chain.doFilter(). That moves to filter 2, and so on, until the servlet runs. The response then travels back through the same filters in reverse order.
A filter can also stop the chain by not calling doFilter(), for example to return 401 for a missing token.
sequenceDiagram
participant C as Client
participant F1 as AuthFilter
participant F2 as LogFilter
participant S as Servlet
C->>F1: request
F1->>F2: doFilter
F2->>S: doFilter
S-->>F2: response
F2-->>F1: response
F1-->>C: response
Order comes from web.xml mapping order, or from registration order in frameworks. Annotation-only filters have no guaranteed order.
Take quiz
by throwing a checked exception in init()
by not calling chain.doFilter()
by calling System.gc()
by renaming the servlet
same as the request order
random order
only through the last filter
reverse of the request order
30. How does Spring AOP use the Proxy pattern?
When a bean has an aspect such as @Transactional or @Cacheable, Spring does not give you the raw bean. It gives you a proxy that runs the advice (start transaction, check cache) and then calls the real method.
If the bean implements an interface, Spring uses a JDK dynamic proxy. Otherwise it uses a CGLIB subclass, so the class and its methods cannot be final.
The classic trap is self-invocation: when a method calls another method on this, it bypasses the proxy, so the second method's @Transactional does nothing.
| JDK dynamic proxy | CGLIB proxy |
| Needs an interface | Subclasses the target class |
| Proxies only interface methods | Cannot override final methods |
Take quiz
transactions only work on static methods
Spring disables them for public methods
the call goes to this, bypassing the proxy
the JVM ignores annotations at runtime
has no interface to proxy
is a record
is a DTO
is declared in XML only
31. How do you implement a thread-safe Singleton?
You have three solid options. The first and simplest is a single-element enum. The second is the initialization-on-demand holder, which is lazy and lock-free because the JVM initialises a class once, safely. The third is double-checked locking with a volatile field, which works but is easy to get wrong.
// Holder idiom public class Registry { private Registry() {} private static class Holder { static final Registry INSTANCE = new Registry(); } public static Registry get() { return Holder.INSTANCE; } } // Double-checked locking public class Cache { private static volatile Cache instance; public static Cache get() { if (instance == null) { synchronized (Cache.class) { if (instance == null) instance = new Cache(); } } return instance; } }
Without volatile, another thread may see a partially constructed object. In a Jakarta EE app, prefer a container-managed @ApplicationScoped bean over any of these.
Take quiz
to make the lock faster
to enable serialization
to allow garbage collection of the class
to prevent seeing a partially constructed instance
it uses a synchronized method on every call
the JVM initialises the nested class once, on first use
it relies on reflection
it creates a thread per call
32. What is the difference between Decorator and Proxy?
Both wrap an object and share its interface, so the code looks almost identical. The difference is intent. A Decorator adds new behaviour or responsibilities, and you can stack several. A Proxy controls access to the object (security, lazy creation, remote call) and usually manages the lifecycle of the real subject itself.
Java I/O is the textbook Decorator: new BufferedReader(new InputStreamReader(in)). Hibernate lazy entities and Spring transactional beans are Proxies.
| Decorator | Proxy |
| Adds responsibilities | Controls access |
| Client usually builds the stack | Proxy often creates or locates the real subject |
| Stackable | Typically one layer |
Take quiz
Decorator
Proxy
Singleton
Flyweight
add stackable extra features
convert one interface to another
control access to the real object
hide a subsystem behind one call
33. When should you use Session Facade?
Use it when a client operation needs several business services or entities to work together in one transaction. A single transferFunds() call is better than the web layer calling debit, credit and audit separately.
It is also right when you want to keep a stable, coarse API for the UI and REST layer while internals change. In remote EJB days it cut network round trips.
Skip it for plain CRUD with one entity, where a facade only forwards calls. And don't dump business rules into the facade. It should orchestrate, while domain objects hold the rules.
Take quiz
to cache static images
one use case spans multiple services in one transaction
to replace the database
to avoid writing tests
transaction boundaries
orchestration of services
a coarse-grained API
detailed domain rules
34. Explain the request lifecycle in Front Controller and MVC?
A request first hits the Front Controller. It applies shared concerns, resolves which controller should handle the URL, and calls it. The controller reads input, calls the service layer, updates the Model, and returns a view name. A view resolver picks the View, which renders the response using model data.
flowchart LR
A[Browser] --> B["Front Controller"]
B --> C["Handler Mapping"]
C --> D[Controller]
D --> E["Service and DAO"]
E --> D
D --> F["Model and View name"]
F --> G["View Resolver"]
G --> H["View renders"]
H --> A
In REST apps the last steps change: the controller returns an object and a message converter writes JSON, so no view resolver is involved.
Take quiz
the DAO
the database driver
handler mapping used by the front controller
the view template
an HTTP message converter writing JSON
a JSP engine
a JDBC row mapper
a thread pool
35. What is the difference between Strategy and State?
The structure is nearly identical: a context delegates to an interface with several implementations. The difference is who changes the implementation and why.
In Strategy, the client picks the algorithm and it usually stays put. In State, the object switches its own behaviour as its state changes, and the states often know which state comes next.
| Strategy | State |
| Client selects the algorithm | Object changes state internally |
| Implementations are independent | States know about transitions |
| Example: shipping cost rule | Example: order Pending, Paid, Shipped, Cancelled |
Take quiz
the garbage collector
the previous state
the database
the client
Strategy
State
Builder
Template Method
36. How does the Command pattern support undo and async work?
Command turns a request into an object with an execute() method. Because it is an object you can store it, queue it, log it, retry it, or hand it to another thread.
For undo, each command also knows how to reverse itself (undo()) or keeps a snapshot, and you push executed commands on a stack. For async work, commands are submitted to an ExecutorService, often a virtual-thread one in Java 21.
interface Command { void execute(); void undo(); } class Deposit implements Command { private final Account a; private final long cents; Deposit(Account a, long cents) { this.a = a; this.cents = cents; } public void execute() { a.add(cents); } public void undo() { a.add(-cents); } } Deque<Command> history = new ArrayDeque<>(); Command c = new Deposit(acc, 500); c.execute(); history.push(c); history.pop().undo();
Take quiz
the request is an object that can be stored and passed around
they are always static
they run only in the database
they cannot hold state
a Set of strings
a single int
a stack of executed commands
a Properties file
37. How do you implement Observer with CDI events?
CDI gives you Observer without writing listener lists. The publisher injects Event<T> and fires an event object. Any bean with a method that has an @Observes parameter of that type is called by the container.
Use fireAsync() with @ObservesAsync for non-blocking delivery. For transaction-aware handling, @Observes(during = TransactionPhase.AFTER_SUCCESS) sends the email only if the order commit worked.
public record OrderPlaced(long orderId) {} @ApplicationScoped class OrderService { @Inject Event<OrderPlaced> events; void place(Order o) { /* save */ events.fire(new OrderPlaced(o.id())); } } @ApplicationScoped class MailListener { void on(@Observes(during = TransactionPhase.AFTER_SUCCESS) OrderPlaced e) { /* send confirmation */ } }
Take quiz
@Inject
@Observes
@Named
@Produces
it rolls back the transaction
it delays JVM shutdown
it makes the event static
the observer runs only if the transaction commits
38. How do Sequenced Collections help build an LRU cache?
Java 21 added SequencedCollection and SequencedMap (JEP 431) with a defined encounter order and methods like firstEntry(), pollFirstEntry(), putLast() and reversed(). LinkedHashMap now implements SequencedMap.
For an LRU cache, the oldest entry sits at the front. On each hit you move the key to the end, and when the size limit is exceeded you remove the first entry. Before Java 21 you needed tricks like removeEldestEntry; now the intent is explicit.
class LruCache<K, V> { private final SequencedMap<K, V> map = new LinkedHashMap<>(); private final int max; LruCache(int max) { this.max = max; } synchronized V get(K k) { V v = map.get(k); if (v != null) map.putLast(k, v); // mark as most recently used return v; } synchronized void put(K k, V v) { map.putLast(k, v); if (map.size() > max) map.pollFirstEntry(); } }
Take quiz
pollFirstEntry
reversed
putLast
clear
JEP 431
JEP 444
JEP 440
JEP 441
39. How does the Circuit Breaker pattern work?
A circuit breaker wraps calls to a remote service and counts failures. It has three states. Closed: calls pass through. Open: after too many failures, calls fail immediately without touching the struggling service. Half-open: after a wait time, a few trial calls are allowed, and success closes the circuit again.
stateDiagram-v2
[*] --> Closed
Closed --> Open: failure threshold reached
Open --> HalfOpen: wait time elapsed
HalfOpen --> Closed: trial calls succeed
HalfOpen --> Open: trial call fails
It protects threads and connection pools from piling up on a slow dependency. Libraries include Resilience4j and MicroProfile Fault Tolerance's @CircuitBreaker, usually paired with a @Fallback.
Take quiz
sent normally
queued forever
retried 100 times each
rejected immediately
shuts down the JVM
allows a few trial calls to test recovery
clears the database
doubles the thread pool
40. How does the Saga pattern manage distributed transactions?
When an operation spans several services with their own databases, a single ACID transaction is not available. A Saga breaks it into a sequence of local transactions. If one step fails, earlier steps are undone by compensating transactions such as refund or release stock.
There are two styles. In choreography, services react to each other's events. In orchestration, a central coordinator tells each service what to do next and triggers compensation on failure.
sequenceDiagram
participant O as Orchestrator
participant I as Inventory
participant P as Payment
O->>I: reserve stock
I-->>O: ok
O->>P: charge card
P-->>O: failed
O->>I: release stock (compensate)
Take quiz
a compensating transaction
a two-phase commit
a JVM restart
a database lock
choreography
serialization
orchestration
delegation
41. Why is Business Delegate rarely used today?
Business Delegate existed to hide JNDI lookups, EJB home interfaces and remote exceptions from the web tier. Modern containers inject local, typed services with @Inject or @Autowired, and remote EJBs are rare, so the delegate would just forward calls.
It still appears where a client talks to a remote API. A small wrapper that adds retry, timeout and error mapping around a REST or gRPC client is a Business Delegate in spirit, even if nobody calls it that.
Take quiz
SQL joins
JNDI lookups and remote EJB details
CSS rules
JVM flags
around every local POJO
inside every record
in a static initializer
wrapping a remote API client with retry and error mapping
42. How does Spring JdbcTemplate use Template Method?
JdbcTemplate owns the fixed parts of JDBC work: get a connection, create the statement, execute, convert SQLException into Spring's unchecked exception hierarchy, and release resources. The part that varies, such as binding parameters or mapping a row, is supplied by you through callbacks like RowMapper.
Strictly it is the Template Method idea implemented with callbacks instead of subclassing. You can't forget to close a connection, because you never open one.
List<User> users = jdbcTemplate.query( "select id, name from users where city = ?", (rs, i) -> new User(rs.getLong("id"), rs.getString("name")), "Charlotte");
Take quiz
a ServletFilter
a Thread factory
a RowMapper callback
a JNDI context
connection handling, execution and exception translation
HTML rendering
class loading
HTTP routing
43. What is the difference between Adapter and Facade?
An Adapter makes an existing class fit an interface that your code expects. It translates one interface into another, usually wrapping one object. A Facade offers a new, simpler interface over a whole subsystem of many classes.
Example: wrapping a vendor's LegacyPay.makePayment(String, int) to implement your PaymentGateway.charge(Money) is an Adapter. A CheckoutFacade.checkout(cart) that calls pricing, tax, stock and payment is a Facade.
| Adapter | Facade |
| Converts interface A to B | Simplifies a subsystem |
| Wraps typically one object | Coordinates many objects |
| Goal: compatibility | Goal: ease of use |
Take quiz
Facade
Singleton
Strategy
Adapter
convert one interface to another
simplify access to a subsystem
create exactly one instance
stack extra behaviour dynamically
44. How do you implement Chain of Responsibility in Java 21?
Each handler either processes the request or passes it to the next one. The simplest Java 21 style is a functional interface plus a list, so there is no linked-handler boilerplate. A sealed result type can state clearly whether a handler consumed the request.
Servlet filters and the Spring Security filter chain use the same idea.
sealed interface Result permits Handled, Pass {} record Handled(String message) implements Result {} record Pass() implements Result {} interface Handler { Result handle(Request r); } Result run(List<Handler> chain, Request r) { for (Handler h : chain) { if (h.handle(r) instanceof Handled done) return done; } return new Handled("no handler matched"); }
Take quiz
passes it to the next handler
throws away the chain
restarts the JVM
logs and closes the server
java.lang.Math
String.format
servlet filter chain
Properties.load
45. How does optimistic locking work with JPA @Version?
You add a @Version field to the entity. When you update, JPA issues UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?. If another transaction changed the row first, zero rows match and JPA throws OptimisticLockException (Spring wraps it as ObjectOptimisticLockingFailureException).
No database lock is held while the user thinks, so it scales well for low-conflict workloads. On conflict you reload and retry, or tell the user.
@Entity public class Account { @Id Long id; @Version long version; BigDecimal balance; }
Take quiz
both succeed silently
the second one fails with OptimisticLockException
the database server restarts
the first is rolled back automatically
workloads where every update conflicts
read-only tables
schemas without primary keys
workloads where conflicts are rare
46. What is the CQRS pattern and when should you use it?
CQRS (Command Query Responsibility Segregation) separates writes from reads. Commands change state and go through the domain model. Queries read from a model shaped for the screen, often a denormalised table or search index.
Use it when read and write loads or shapes differ a lot, for example a heavy dashboard over data written by simple transactions. The cost is complexity and, if the read store is updated asynchronously, eventual consistency.
Don't apply it to a simple CRUD screen. A single model is easier to build and debug.
| Side | Responsibility |
| Command | Validate, change state, return little or nothing |
| Query | Return data, never change state |
Take quiz
always modify the write model
must use two-phase commit
only read data and do not change state
can only be run at night
eventual consistency
lack of any database
loss of HTTP support
inability to use records
47. What is the difference between Event Sourcing and CRUD?
In CRUD you store the current state and overwrite it on every update. In Event Sourcing you store every state change as an immutable event (OrderPlaced, ItemAdded) and rebuild the current state by replaying them.
You get a full audit trail, the ability to rebuild past states and to create new read models later. You pay with harder queries, event versioning, and a mindset change. It often pairs with CQRS.
| CRUD | Event Sourcing |
| Stores latest state | Stores sequence of events |
| Updates overwrite data | Events are append-only |
| History needs extra audit tables | History is built in |
Take quiz
only the latest row
only HTML pages
only session cookies
an append-only sequence of events
by reading a single overwritten row
by replaying events
by asking the browser
by recompiling the code
48. How do Scoped Values replace ThreadLocal for request context?
ThreadLocal is mutable, lives as long as the thread unless you remove it, and each child thread copies inheritable values. With millions of virtual threads that gets expensive and leak-prone.
ScopedValue is immutable and bound only for the duration of a run() or call() block, then it disappears automatically. In Java 21 it is a preview API (JEP 446), so you need --enable-preview.
static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance(); void handle(Request r) { ScopedValue.where(CURRENT_USER, authenticate(r)) .run(() -> service.process(r)); } // inside service.process(): CURRENT_USER.get()
Take quiz
a preview API
removed from the JDK
a finalised API since Java 8
an annotation only
until the JVM exits
until the thread is garbage collected
only during the run or call block
forever once set
49. Which classic J2EE patterns are obsolete today?
Several patterns existed to work around limits of EJB 2.x, and they have faded. Service Locator and Business Delegate are replaced by injection. Composite Entity and Transfer Object Assembler mostly went away with JPA entities and projections. Value List Handler became paging queries.
Patterns that still matter are Front Controller (every web framework), Intercepting Filter, DAO or Repository, Session Facade as an application service, and Transfer Object, now written as records.
| Pattern | Today |
| Service Locator | Replaced by CDI or Spring injection |
| Composite Entity | Replaced by JPA entities |
| Value List Handler | Replaced by paging queries |
| Front Controller | Still used, built into frameworks |
Take quiz
Front Controller
Service Locator
Intercepting Filter
Observer
Composite Entity
Business Delegate for local calls
Value List Handler with EJB 2 cursors
Front Controller
50. How do you avoid over-engineering with design patterns?
Start with the simplest code that works and introduce a pattern only when a real pain appears: duplicated branches, hard-to-test dependencies, or a class that changes for several reasons. A pattern should remove a problem you can name.
Java 21 helps here. A lambda often replaces a Strategy class, a record replaces a DTO class, and a sealed switch replaces a Visitor. Always ask what the pattern costs in indirection, and whether a teammate can follow the flow in a debugger.
A useful test is to delete the pattern in your head. If the code gets shorter and no requirement is lost, it was not needed.