Throwable is the root of everything that can be thrown. It has two direct subclasses: Error (serious JVM or environment problems) and Exception. RuntimeException extends Exception.
Error and RuntimeException (with their subclasses) are unchecked.
All other Exception subclasses, and Throwable itself, are checked.
Only Throwable instances can appear in throw and catch.
⚠ Follow-up traps
Is Throwable checked? Yes, anything not under Error or RuntimeException is checked, including Throwable itself.
Can you throw a class that is not a Throwable? No, compile error.
#throwable#hierarchy
Q2
What is the difference between checked and unchecked exceptions?
basic
Checked exceptions must be caught or declared in throws; the compiler enforces it. Unchecked exceptions (RuntimeException, Error) are not enforced.
Checked: recoverable conditions the caller can reasonably handle (I/O, parsing external data).
Unchecked: programming errors (NullPointerException, IllegalArgumentException) or unrecoverable failures.
Checking exists only at compile time; the JVM does not distinguish them.
⚠ Follow-up traps
Does the JVM enforce checked exceptions? No, it is purely a compiler rule; sneaky-throw tricks bypass it.
Is Exception itself checked? Yes.
#checked#unchecked
Q3
When should you use a checked vs an unchecked exception?
intermediate
Use checked when the caller can realistically recover and you want to force them to consider it; use unchecked for precondition violations and for failures the caller cannot meaningfully handle.
Modern libraries (Spring, Hibernate) prefer unchecked exceptions because checked ones leak through layers and pollute signatures.
Checked exceptions do not compose well with lambdas and streams.
Never choose checked just to "make callers careful"; they tend to wrap or swallow.
⚠ Follow-up traps
Are checked exceptions always better for recoverable errors? No; if every caller just wraps and rethrows, the benefit is lost.
Which one does Optional.orElseThrow favour? It accepts any supplier, so the choice is yours.
#design#checked#unchecked
Q4
How does try/catch/finally execute?
basic
The try block runs; if it throws, the first matching catch (top to bottom) runs; finally runs afterwards in every case, whether or not an exception occurred or was caught.
If no catch matches, finally still runs and the exception then propagates.
A catch for a superclass placed before a subclass is a compile error (unreachable).
Only System.exit, Runtime.halt, JVM crash or thread death/never-ending loop prevent finally.
⚠ Follow-up traps
Does finally run if the try block returns? Yes, after the return expression is evaluated.
Does finally run after System.exit(0)? No.
#try#catch#finally
Q5
What happens when you `return` inside `finally`?
intermediate
A return in finally overrides any earlier return value and discards any exception in flight. It is considered a serious bug.
static int f() { try { throw new RuntimeException("x"); } finally { return 1; } // exception swallowed, returns 1}
The same applies to break and continue that leave the finally block abruptly.
javac warns with -Xlint:finally; static analysis tools flag it.
⚠ Follow-up traps
Is the exception logged anywhere? No, it silently vanishes.
Does it compile? Yes, only a lint warning.
#finally#return#masking
Q6
If try returns a primitive and finally modifies the variable, what is returned?
intermediate
The return value is evaluated and saved before finally runs, so a later modification of a primitive local does not affect it.
static int f() { int x = 1; try { return x; } finally { x = 2; }}// returns 1
For a reference type the reference is saved, but mutations of the object itself are visible to the caller.
⚠ Follow-up traps
What if x were a StringBuilder and finally appended to it? The caller sees the appended content.
What if finally also does return x? The finally value wins (2).
#finally#return#tricky
Q7
How can an exception be masked or lost?
intermediate
If a finally block (or a close in older code) throws, its exception replaces the one thrown from the try block; the original is lost without any trace.
Also lost by return/break in finally, empty catch blocks, and catching then throwing a new exception without the cause.
try-with-resources fixes the first case by attaching the close failure as a suppressed exception.
⚠ Follow-up traps
Which exception does the caller see if both try and finally throw? The one from finally.
Can the lost exception be recovered? Not in a hand-written finally unless you saved and addSuppressed it.
#masking#finally#suppressed
Q8
What is try-with-resources and what does it require?
basic
It declares resources in the try header; each is closed automatically in reverse order of declaration when the block exits, normally or exceptionally. Resources must implement AutoCloseable.
Closeable extends AutoCloseable; its close() throws IOException and is idempotent by contract.
Java 9+ allows an effectively final existing variable: try (r) { ... }.
Null resources are skipped without NPE.
⚠ Follow-up traps
Order of closing? Reverse of declaration.
Can you use a non-final variable in the header? No, it must be final or effectively final (Java 9+).
#try-with-resources#autocloseable
Q9
How do suppressed exceptions work?
intermediate
If the try body throws and close() also throws, the body exception is the primary one and the close exception is attached via addSuppressed. Retrieve with getSuppressed().
If only close() throws, that exception is primary.
Multiple resources can each add a suppressed exception.
printStackTrace shows them under "Suppressed:".
⚠ Follow-up traps
Which exception is thrown if body and close both fail? The body exception.
Can you disable suppression? Yes via the protected Throwable constructor with enableSuppression=false.
#suppressed#try-with-resources
Q10
In what order do the catch and close happen in try-with-resources?
intermediate
Resources are closed before any catch or finally block of the same statement runs.
Sequence: resource init, body, close resources (reverse order), catch, finally.
The catch therefore cannot use the resource (it is out of scope and already closed).
Exceptions thrown from close are catchable by that same try statement's catch clauses.
⚠ Follow-up traps
Is the resource open inside catch? No, already closed.
Is an exception in the resource initializer caught by the same catch? Yes.
#try-with-resources#ordering
Q11
What is multi-catch and what are its restrictions?
basic
Java 7 allows catch (IOException | SQLException e) to share one handler. The parameter is implicitly final.
Alternatives cannot be related by subclassing (IOException | FileNotFoundException is a compile error).
The static type of e is the least upper bound of the alternatives.
⚠ Follow-up traps
Can you reassign e? No, it is implicitly final.
Why is a subclass alternative disallowed? It would be redundant.
#multi-catch#java7
Q12
What is precise rethrow?
advanced
Since Java 7, if a catch parameter is effectively final and you throw e, the compiler treats the thrown types as the checked exceptions the try block can actually throw, not the declared catch type.
Does it compile on Java 6? No, it would require throws Exception.
Does the try body throwing only unchecked make throws unnecessary? Yes.
#rethrow#java7
Q13
What does `throw` vs `throws` mean?
basic
throw is a statement that throws one exception instance. throws is a method-signature clause declaring checked exception types the method may propagate.
throws is part of the method contract, not the signature for overloading.
Overriding methods cannot throw broader checked exceptions than the overridden one.
⚠ Follow-up traps
Can an override throw a broader unchecked exception? Yes, unchecked is unrestricted.
Can throws list unchecked exceptions? Yes, as documentation only.
#throw#throws
Q14
What are the rules for exceptions in overriding?
intermediate
An overriding method may throw no new or broader checked exceptions than the overridden method; it may throw fewer, narrower, or none. Unchecked exceptions are unrestricted.
Constructors are not overridden, but a subclass constructor must declare any checked exception from the super constructor it calls.
The rule maintains substitutability through the supertype reference.
⚠ Follow-up traps
Can an interface method with no throws be implemented with throws IOException? No.
Can the override drop the throws clause? Yes.
#overriding#throws
Q15
What are chained causes and how do you use them?
basic
An exception can wrap another via new MyException("msg", cause) or initCause. getCause() returns it, and stack traces print "Caused by:" sections.
Always preserve the cause when translating exceptions; losing it destroys the root-cause trace.
initCause can be called once, only if the cause was not set by the constructor.
⚠ Follow-up traps
Can initCause(this) be used? No, IllegalArgumentException (self-causation).
Does new RuntimeException(e) set the message? Yes, to e.toString().
#cause#chaining
Q16
What does getMessage() vs toString() vs getLocalizedMessage() return?
basic
getMessage() returns the detail message (may be null). toString() returns ClassName: message (or just class name when message is null). getLocalizedMessage() defaults to getMessage() and can be overridden for i18n.
Logging e.getMessage() alone loses the type and stack trace; NullPointerException often has a null or helpful message depending on version.
⚠ Follow-up traps
Can getMessage() return null? Yes.
Does logging the message give you the stack trace? No.
#message#tostring
Q17
What are helpful NullPointerException messages?
intermediate
Since Java 14 (default on from 15), the JVM computes a message describing what was null, such as Cannot invoke "String.length()" because "s" is null.
Local variable names appear only if compiled with -g; otherwise <local1>.
Message is computed lazily on getMessage().
⚠ Follow-up traps
Does it cost runtime when the NPE is thrown? Computation is lazy so negligible until message is read.
Can you disable it?-XX:-ShowCodeDetailsInExceptionMessages.
#npe#java14
Q18
What is the difference between Error and Exception?
basic
Error signals abnormal conditions from the JVM or environment (OutOfMemoryError, StackOverflowError, NoClassDefFoundError) that applications generally should not catch. Exception covers conditions an application may handle.
Catching Error is allowed technically, but program state may be inconsistent.
Legitimate cases: logging and graceful shutdown, a container isolating tasks, catching StackOverflowError in a recursive parser guard.
⚠ Follow-up traps
Is AssertionError an Error? Yes.
Should you catch Throwable? Only at top-level boundaries, and rethrow or exit for fatal errors.
#error#exception
Q19
What causes StackOverflowError and how do you handle it?
intermediate
Thread stack exhaustion, usually from unbounded or very deep recursion. Each call pushes a frame; the stack size is set by -Xss (default ~512KB-1MB).
Fix by adding a base case, converting to iteration, or using an explicit stack.
Increasing -Xss only delays it, and costs memory per thread.
Also caused by cyclic toString/hashCode/equals between bidirectional JPA entities or Lombok.
⚠ Follow-up traps
Does Java do tail-call optimization? No.
Can you catch it? Yes, but state may be corrupt; do it only at a boundary.
#stackoverflowerror#recursion
Q20
What causes OutOfMemoryError and what kinds exist?
intermediate
OOM is thrown when the JVM cannot allocate memory and GC cannot free enough. The message identifies the area.
Java heap space: live data exceeds -Xmx or a leak.
GC overhead limit exceeded: GC consumes nearly all time recovering little.
Metaspace: too many classes (classloader leak).
unable to create native thread: OS thread/memory limit.
Direct buffer memory: off-heap NIO limit.
Use -XX:+HeapDumpOnOutOfMemoryError to capture a dump for analysis.
⚠ Follow-up traps
Does OOM always kill the JVM? No, only the allocating thread gets the error; other threads may continue in a bad state.
Is raising -Xmx a fix for a leak? No, only postpones it.
#oom#heap#metaspace
Q21
What is the performance cost of exceptions?
advanced
Creating a Throwable is expensive because fillInStackTrace() walks and records the whole stack; throwing and unwinding is comparatively cheap, especially when the JIT inlines the throw and catch.
Cost grows with stack depth (deep Spring/servlet stacks make it worse).
Avoid exceptions for normal control flow in hot paths.
Mitigate: reuse preallocated exceptions, override fillInStackTrace, or use the 4-arg constructor with writableStackTrace=false.
⚠ Follow-up traps
Is the try block itself costly when nothing is thrown? No, zero-cost on the happy path (exception tables).
Is throwing slower than creating? Usually creating dominates.
#performance#stacktrace
Q22
How do you design a custom exception class?
intermediate
Extend RuntimeException (or Exception for recoverable checked cases), provide constructors for message and message+cause, and add structured fields like an error code instead of parsing messages.
public class OrderNotFoundException extends RuntimeException { private final long orderId; public OrderNotFoundException(long orderId) { super("Order not found: " + orderId); this.orderId = orderId; } public long getOrderId() { return orderId; }}
Keep a small, meaningful hierarchy; one base type per module.
Exceptions are Serializable; keep fields serializable or transient.
⚠ Follow-up traps
Should you add a serialVersionUID? Recommended for stable serialization.
Should every error get its own class? No, use codes/enums when callers do not branch on type.
#custom-exception#design
Q23
What is exception translation (wrapping)?
intermediate
Catching a low-level exception and throwing a higher-level one appropriate to the abstraction, with the original as cause. Example: SQLException becomes DataAccessException.
Prevents leaking implementation details (JDBC, HTTP client) through layers.
Spring's @Repository plus PersistenceExceptionTranslationPostProcessor translates persistence exceptions automatically.
Always pass the cause.
⚠ Follow-up traps
Difference between translation and chaining? Chaining is the mechanism (cause); translation is the design practice.
Should every layer wrap? No, wrap only at abstraction boundaries.
#translation#layers
Q24
Why is catching `Exception` or `Throwable` generally discouraged?
basic
It hides bugs, catches unexpected unchecked exceptions, may catch InterruptedException and Error, and prevents specific recovery.
Acceptable at boundaries: request handlers, thread run loops, scheduled jobs, where you must log and continue.
Catch the narrowest type you can handle.
⚠ Follow-up traps
Does catch (Exception e) catch OutOfMemoryError? No, it is an Error.
Does it catch InterruptedException? Yes, and swallowing it clears the thread's interrupt intent.
#catch#antipattern
Q25
How should InterruptedException be handled?
intermediate
Either propagate it or restore the flag with Thread.currentThread().interrupt() before returning; never swallow it.
Throwing InterruptedException clears the interrupt flag, so catching it without restoring loses the signal.
⚠ Follow-up traps
Does Thread.interrupted() clear the flag? Yes; isInterrupted() does not.
Is ignoring it OK in a Runnable? No, executors rely on interruption for cancellation.
#interrupted#threads
Q26
What is an unchecked exception thrown by a static initializer?
advanced
An exception escaping a static initializer is wrapped in ExceptionInInitializerError; the class is then unusable, and later access throws NoClassDefFoundError.
ExceptionInInitializerError carries the real exception as its cause.
The first failure shows the root; subsequent ones do not, making logs misleading.
⚠ Follow-up traps
Is the original exception rewrapped if it is already an Error? No, Error types propagate as-is.
Can the class be re-initialized? No.
#static-init#noclassdeffounderror
Q27
How do you handle checked exceptions inside lambdas?
intermediate
Standard functional interfaces do not declare checked exceptions, so a lambda body cannot throw them. Catch inside the lambda and wrap in an unchecked exception, or define your own functional interface that throws.
Callable is the JDK interface that allows checked exceptions.
⚠ Follow-up traps
Does Callable fit stream.map? No, map takes Function.
What does the wrapper do to the stack trace? Adds a "Caused by" layer; keep the cause.
#lambda#checked#streams
Q28
What happens when an exception is thrown in a stream pipeline?
intermediate
The terminal operation propagates the exception and the pipeline stops; remaining elements are not processed (sequential case). Intermediate operations are lazy, so the exception surfaces at the terminal operation, not where the lambda is written.
In parallel streams, one exception propagates to the caller and others may be lost or suppressed; side effects of other elements may or may not have occurred.
No built-in per-element error handling; map to a result type (Either/Optional) when you need to continue.
⚠ Follow-up traps
Where does the stack trace show the throw? In the terminal operation call chain through stream internals.
Does a parallel stream guarantee which exception you see? No.
#streams#exception
Q29
What happens to an uncaught exception in a thread?
basic
The thread terminates; the exception is passed to the thread's UncaughtExceptionHandler (default prints the stack trace to stderr). Other threads, including main, keep running.
If main throws, the JVM still waits for other non-daemon threads.
The exception does not propagate to the thread that started it.
⚠ Follow-up traps
Can try/catch around thread.start() catch it? No.
Does the JVM exit when the only user thread dies from exception? Yes, if no other non-daemon threads exist.
#threads#uncaught
Q30
How do UncaughtExceptionHandlers work?
intermediate
Lookup order: per-thread handler (setUncaughtExceptionHandler), then the thread group, then the global default (Thread.setDefaultUncaughtExceptionHandler).
Use the default handler to log failures from any thread.
It is invoked after the thread's run has been abandoned; it cannot resume the thread.
Not triggered for exceptions captured in a Future.
⚠ Follow-up traps
Does the default handler run for executor.submit tasks? No, exceptions are stored in the Future.
Does it run for execute() tasks? Yes, the worker thread dies and the handler is invoked.
#uncaught#handler
Q31
How do exceptions behave in ExecutorService submit vs execute?
intermediate
execute(Runnable) lets the exception escape to the worker's uncaught handler. submit wraps the task in a FutureTask that captures the exception, surfacing it only when you call get() as ExecutionException.
Fire-and-forget submit silently loses failures.
ThreadPoolExecutor replaces a worker that died from execute.
Override afterExecute or wrap tasks in logging for centralized handling.
⚠ Follow-up traps
What does future.get() throw if the task threw IOException?ExecutionException with the IOException as cause.
Does a swallowed submit exception kill the pool thread? No.
#executor#future
Q32
What happens when a ScheduledExecutorService task throws?
advanced
For periodic tasks (scheduleAtFixedRate / WithFixedDelay) an uncaught exception cancels all subsequent executions, silently, because the failure is stored in the Future.
Wrap the task body in try/catch (Throwable), log, and continue.
Spring @Scheduled methods are wrapped and an error is logged, but later runs continue.
⚠ Follow-up traps
Is a log message produced by default? No, not for raw ScheduledExecutorService.
Does a one-shot schedule behave differently? The exception sits in the returned Future.
#scheduled#executor
Q33
How are exceptions handled in CompletableFuture?
intermediate
An exception in a stage completes it exceptionally and propagates to dependent stages, wrapped in CompletionException. Handle with exceptionally, handle or whenComplete.
exceptionally(ex -> fallback) recovers; handle((v, ex) -> ...) sees both outcomes; whenComplete observes but does not change the result.
In exceptionally downstream of the source stage the exception is a CompletionException wrapper; the original is getCause().
⚠ Follow-up traps
Does whenComplete swallow the exception? No, it propagates unless the action itself throws.
What if no one handles it? It is silently kept; no log appears.
#completablefuture#async
Q34
What is the difference between final, finally and finalize?
basic
final is a modifier (constant, non-overridable, non-subclassable). finally is the block that runs after try/catch. finalize() was an Object method called by GC before reclamation; it is deprecated for removal (Java 18+).
Use try-with-resources or Cleaner instead of finalizers.
Finalizers are unpredictable, slow GC and can resurrect objects.
⚠ Follow-up traps
Is finalize guaranteed to run? No.
What replaced it?java.lang.ref.Cleaner and PhantomReference.
#finally#finalize
Q35
What are common exception logging pitfalls?
intermediate
Typical mistakes: log and rethrow (duplicate entries), e.printStackTrace(), logging e.getMessage() only, and passing the exception as a string concatenation so the stack trace is lost.
log.error("Failed to process order {}", id, e); // good: last arg is Throwablelog.error("Failed: " + e); // bad: no stack trace
Log once, at the boundary that handles the exception.
Do not log sensitive data in messages (PII, tokens).
With SLF4J, a trailing Throwable not matching a placeholder is printed as the stack trace.
⚠ Follow-up traps
Why is log-and-rethrow bad? The same failure appears several times, making incidents hard to read.
Is printStackTrace() acceptable? No, it bypasses the logging framework and goes to stderr.
#logging#antipattern
Q36
What is the "swallowing exceptions" antipattern?
basic
An empty or log-less catch block hides failures, leaving the program in an unknown state and making debugging nearly impossible.
If ignoring is intentional, comment why and name the variable ignored.
Prefer handling, rethrowing with context, or failing fast.
⚠ Follow-up traps
Is catch (Exception e) {} ever correct? Very rarely, e.g. best-effort cleanup, and then log at debug.
Is returning null from catch the same problem? Yes, it moves the failure downstream.
#swallow#antipattern
Q37
What is fail-fast and how does it relate to exceptions?
basic
Detect invalid state at the earliest point and throw immediately rather than propagating bad data. Validate arguments at method entry.
IllegalArgumentException: bad input. IllegalStateException: object is in the wrong state for the call.
UnsupportedOperationException: operation not supported (e.g. immutable collection).
⚠ Follow-up traps
IAE vs ISE? Argument is wrong vs target state is wrong.
Does requireNonNull throw a checked exception? No, NPE.
#fail-fast#validation
Q38
What is the difference between NoClassDefFoundError and ClassNotFoundException?
intermediate
ClassNotFoundException is a checked exception from explicit loading (Class.forName, loadClass). NoClassDefFoundError is an Error thrown when a class present at compile time is missing at runtime, or its static init failed earlier.
Typical cause: missing dependency in the runtime classpath, version conflicts, or failed static initializer.
⚠ Follow-up traps
Which is checked?ClassNotFoundException.
Can a class that exists on disk cause NoClassDefFoundError? Yes, after an ExceptionInInitializerError.
#classloading#error
Q39
What does `assert` do and how does it differ from exceptions?
basic
assert cond : msg; throws AssertionError if the condition is false, but only when assertions are enabled with -ea. They are disabled by default.
Use for internal invariants, never for validating public method arguments or anything with side effects.
⚠ Follow-up traps
Are assertions on in production by default? No.
Should you catch AssertionError? No.
#assert#assertionerror
Q40
How does `Optional` relate to exception handling?
intermediate
Optional models an absent value without exceptions. orElseThrow(Supplier) converts absence into an exception at the point you choose.
User u = repo.findById(id) .orElseThrow(() -> new UserNotFoundException(id));
Does the supplier run when a value is present? No, it is lazy.
Should repository methods that expect a result return Optional? Yes, when absence is a legitimate outcome.
#optional#design
Q41
What does the stack trace contain and how can you control it?
intermediate
A snapshot of the call stack at construction (not at throw) time, as StackTraceElement[], with "Caused by" chains and "... n more" for frames shared with the enclosing trace.
setStackTrace replaces it; getStackTrace returns a copy.
JIT may omit stack traces for hot, frequently thrown built-in exceptions (-XX:-OmitStackTraceInFastThrow disables that).
Stack trace depth is limited by -XX:MaxJavaStackTraceDepth.
⚠ Follow-up traps
Where does the trace point if you create an exception in one place and throw it elsewhere? At creation.
Why do logs sometimes show NPE with an empty stack trace?OmitStackTraceInFastThrow after many repeated throws.
#stacktrace#diagnostics
Q42
What is a good REST error-handling strategy in Spring Boot?
intermediate
Throw domain exceptions from services and translate them centrally with @RestControllerAdvice and @ExceptionHandler into a consistent error body and HTTP status.
Add a catch-all handler returning 500 with a generic message, logging the details with a correlation id.
⚠ Follow-up traps
Should the 500 body include the stack trace? No, it leaks internals.
Does @ControllerAdvice apply to filter exceptions? No, filters run outside DispatcherServlet.
#rest#spring#controlleradvice
Q43
What is ProblemDetail (RFC 7807 / RFC 9457)?
intermediate
A standard JSON error format (application/problem+json) with type, title, status, detail, instance and extension members. Spring 6 / Boot 3 provides ProblemDetail and ErrorResponse.
Enable built-in handling for Spring MVC exceptions with spring.mvc.problemdetails.enabled=true.
Or extend ResponseEntityExceptionHandler in your advice.
Use setProperty to add custom fields such as error codes.
⚠ Follow-up traps
Is the media type application/json? No, application/problem+json.
Is ProblemDetail available on Spring 5? No, Spring 6 / Boot 3.
#rest#problemdetail#spring6
Q44
Which HTTP status codes should map to which exceptions?
basic
Client mistakes map to 4xx, server faults to 5xx.
400: malformed or invalid input (validation, MethodArgumentNotValidException).
401 / 403: unauthenticated / unauthorized.
404: resource not found. 409: state conflict (duplicate, optimistic lock).
422: semantically invalid payload (optional, team convention).
500: unexpected bug. 503: dependency down or overloaded.
⚠ Follow-up traps
401 vs 403? 401 means identity unknown; 403 means identity known but not permitted.
Which code for optimistic lock failure? 409 typically.
#rest#http-status
Q45
How does Spring resolve exceptions in MVC?
advanced
DispatcherServlet delegates to HandlerExceptionResolvers in order: ExceptionHandlerExceptionResolver (@ExceptionHandler), ResponseStatusExceptionResolver (@ResponseStatus / ResponseStatusException), DefaultHandlerExceptionResolver (standard Spring exceptions).
Controller-local @ExceptionHandler takes precedence over advice.
The most specific exception type match wins.
Unresolved exceptions go to the container error page (/error in Boot, BasicErrorController).
⚠ Follow-up traps
Local vs global handler? Local controller handler is preferred.
Does Boot's /error show messages by default? Not since Boot 2.3; server.error.include-message opts in.
#spring#resolver
Q46
How does bean validation exception handling work?
intermediate
@Valid on @RequestBody throws MethodArgumentNotValidException (contains BindingResult); constraints on @RequestParam/@PathVariable with @Validated throw ConstraintViolationException (Boot 3 / Spring 6.1+ raises HandlerMethodValidationException for method validation).
Map both to 400 and return field-level errors.
Do not expose raw validation internals; map to field and message.
⚠ Follow-up traps
Are they the same exception? No, body binding vs method constraint validation.
Is @Validated on the controller class needed for @RequestBody @Valid? No.
#validation#spring
Q47
When does a Spring `@Transactional` method roll back?
intermediate
By default, a transaction rolls back on unchecked exceptions (RuntimeException and Error) and commits on checked exceptions.
Override with rollbackFor = Exception.class or noRollbackFor.
The exception must propagate out of the transactional proxy; if you catch it inside the method, the transaction commits.
⚠ Follow-up traps
Does a checked exception roll back? No, not by default.
Does a caught exception roll back? No, unless you call setRollbackOnly().
#transactions#rollback
Q48
What is the rollback-only trap with nested transactional calls?
advanced
With REQUIRED propagation, an inner @Transactional method that throws marks the shared transaction rollback-only; even if the outer method catches it, the commit fails with UnexpectedRollbackException.
Use REQUIRES_NEW for work that must succeed or fail independently (separate connection).
Or avoid throwing across the boundary and return a result.
⚠ Follow-up traps
Does catching the exception in the outer method save the transaction? No, it is already rollback-only.
Does REQUIRES_NEW use the same connection? No, a new connection; the outer one is suspended.
#transactions#rollback-only#propagation
Q49
Why does `@Transactional` not roll back for self-invocation or private methods?
advanced
Spring applies transactions through proxies. A call from within the same class bypasses the proxy, so no transactional advice runs, and rollback rules never apply. Private methods are not proxied.
Fix by moving the method to another bean, injecting self, or using AspectJ mode.
Also, the method must be public for default proxy-based advice.
⚠ Follow-up traps
Does the exception still propagate? Yes, but nothing rolls back at that method boundary.
Does final on a method matter for CGLIB proxies? Yes, it cannot be intercepted.
#transactions#proxy
Q50
How do JDBC and JPA exceptions differ and how does Spring translate them?
intermediate
JDBC throws checked SQLException with vendor codes; JPA throws unchecked PersistenceException subclasses. Spring translates both to the DataAccessException hierarchy (DuplicateKeyException, DataIntegrityViolationException, OptimisticLockingFailureException).
Translation is applied to @Repository beans via a post processor.
Services can catch Spring types without depending on the persistence technology.
⚠ Follow-up traps
Is DataAccessException checked? No, it is a RuntimeException.
Why might a flush-time constraint violation surface late? JPA defers SQL until flush or commit.
#jdbc#jpa#translation
Q51
What is the difference between `Exception.getCause()` root cause search and `NestedExceptionUtils`?
intermediate
getCause() returns only the immediate cause. To reach the root cause, loop until getCause() is null (guarding against cycles); Spring offers NestedExceptionUtils.getRootCause and Apache Commons has ExceptionUtils.getRootCause.
static Throwable root(Throwable t) { Throwable r = t; while (r.getCause() != null && r.getCause() != r) r = r.getCause(); return r;}
⚠ Follow-up traps
Can a cause chain be cyclic? Possible if built manually; guard with a visited set.
Does printStackTrace loop forever on cycles? No, it detects cycles ("CIRCULAR REFERENCE").
#cause#root-cause
Q52
How do you retry safely after exceptions?
advanced
Retry only transient, idempotent failures, with a bounded attempt count, exponential backoff and jitter, and surface the final exception with earlier ones as suppressed.
Spring Retry (@Retryable) or Resilience4j; combine with a circuit breaker to avoid hammering a failing dependency.
Never retry validation errors or non-idempotent calls without idempotency keys.
Retrying inside a @Transactional method retries within a rollback-only transaction; retry outside the transaction boundary.
⚠ Follow-up traps
Why jitter? To avoid synchronized retry storms.
Is retrying a POST safe? Only with an idempotency key.
#retry#resilience
Q53
What are `Error` handling guidelines for a long-running service?
advanced
Log fatal Errors and let the process die so an orchestrator restarts it, rather than continuing in an undefined state. For OOM, use -XX:+ExitOnOutOfMemoryError or -XX:+CrashOnOutOfMemoryError.
A thread that hit OOM may have left locks or half-updated shared state.
Catching Throwable in task loops is fine only if you rethrow VirtualMachineError and ThreadDeath-like fatals.
⚠ Follow-up traps
Does ExitOnOutOfMemoryError run shutdown hooks? It exits the JVM; use CrashOnOutOfMemoryError when you also want a core dump.
Is it safe to continue after StackOverflowError? Usually yes if unwound cleanly, but not guaranteed.
#error#resilience#jvm
Scenarios
Q54
What does this print and return?
basic
It prints 2. The return in finally replaces the value saved by the try block.
The compiler accepts it (lint warning only).
Same override happens for exceptions: finally { return 2; } swallows them.
⚠ Follow-up traps
What if finally only assigned a local int? Still returns 1, the value is already captured.
Does javac fail the build? No, unless -Werror with -Xlint:finally.
#finally#return#tricky-output
Q55
What is the output of this method?
basic
ABCD. The catch returns, but finally still runs (C) before control goes back to the caller, which then prints D.
⚠ Follow-up traps
If System.exit(0) replaced return? Output is AB; finally does not run.
If the finally block threw? That exception would replace the return.
#finally#catch#return#tricky-output
Q56
Which exception does the caller see here?
intermediate
The caller gets IllegalArgumentException: finally. The IllegalStateException is lost completely; there is no cause and no suppressed link.
Fix by catching in finally and addSuppressed, or by using try-with-resources.
This is exactly why cleanup code in finally should not throw.
⚠ Follow-up traps
Does the stack trace mention "try"? No.
Does try-with-resources behave the same way? No, it keeps the body exception and suppresses the close exception.
#finally#masking#tricky-output
Q57
What is the output order of this try-with-resources?
basic
open 1open 2bodyclose 2close 1
Resources are closed in reverse order of creation.
⚠ Follow-up traps
If the body throws, are resources still closed? Yes, before any catch block runs.
Does close() here need throws Exception? No, the override narrows it away.
#try-with-resources#ordering#tricky-output
Q58
What does this print about suppressed exceptions?
intermediate
body 1 close. The body exception is primary; the close exception is added as suppressed.
⚠ Follow-up traps
What if the body did not throw? The caught exception is the close one with zero suppressed.
What changes if you use try/finally with r.close()? The caller would see only the finally exception.
#suppressed#try-with-resources#tricky-output
Q59
Trace the output of nested try/finally.
basic
F1 C:a F2. The inner finally runs while the exception unwinds, the outer catch handles it, then the outer finally runs.
⚠ Follow-up traps
Does the outer catch see the exception if the inner finally completes normally? Yes, it keeps propagating.
What if the inner finally returned? The exception would be swallowed and the outer catch would not run.
#finally#nested#tricky-output
Q60
Will this compile?
basic
No. NumberFormatException extends IllegalArgumentException, so the second catch is unreachable and javac reports "exception has already been caught". Put the subclass first.
⚠ Follow-up traps
Is the reverse order legal? Yes, specific first, general later.
Is an unreachable catch for an unrelated checked exception legal? No, javac errors if the try body cannot throw that checked exception.
#catch-order#compile-error
Q61
A class has a static initializer that throws. What do callers see?
advanced
The first access to Config throws ExceptionInInitializerError (cause: IllegalStateException). Every later access throws NoClassDefFoundError: Could not initialize class Config, whose trace hides the real cause.
Look for the first occurrence in logs to find the root cause.
Avoid risky work in static initializers; use lazy holders with explicit error handling.
⚠ Follow-up traps
Can a retry succeed after the file is fixed? No, the class stays in the erroneous state until the classloader is discarded.
Does a checked exception work in a static initializer? It cannot propagate; you must catch it.
#static-init#noclassdeffounderror
Q62
An exception happens inside `stream.map` during a `forEach`. What gets processed?
intermediate
Prints 1 and 2, then throws NumberFormatException for x; 4 is never processed. The pipeline is lazy and processed element by element, so earlier elements are already printed.
To keep going, catch inside the mapper and return an Optional or result object, then filter.
⚠ Follow-up traps
Would collect(toList()) have returned a partial list? No, the exception discards it.
What if there were no terminal operation? Nothing runs, no exception.
#streams#exception#tricky-output
Q63
A task submitted to an executor throws, but nothing appears in the logs. Why?
intermediate
submit captures the exception in the returned Future; no one calls get(), so it is never reported and the uncaught handler is not invoked.
Use execute, or wrap the task body in try/catch with logging, or inspect Future.get().
For centralized handling, subclass ThreadPoolExecutor and override afterExecute, which receives the Throwable for execute tasks and requires unwrapping the Future for submit.
⚠ Follow-up traps
Does the pool thread die? No.
What does get() throw?ExecutionException with IllegalStateException as the cause.
#executor#future#debugging
Q64
A scheduled job stops running after one failure. Why?
intermediate
An exception from a periodic task suppresses all subsequent executions, and it is stored silently in the ScheduledFuture. Wrap the body in try/catch(Exception) and log.
Does Spring @Scheduled behave the same? No, Spring wraps the task with an error handler that logs and continues.
Should you catch Error too? Usually not; let fatal errors surface.
#scheduled#executor#debugging
Q65
What happens to the program when a worker thread throws?
basic
main done is printed and the stack trace for RuntimeException: t goes to stderr (order may vary). The JVM exits normally with code 0 since all non-daemon threads finished.
Exit code 0 even though a thread failed; monitoring must not rely on it.
⚠ Follow-up traps
Can main catch it? No.
How to log it centrally?Thread.setDefaultUncaughtExceptionHandler.
#threads#uncaught#tricky-output
Q66
Does this transaction roll back?
intermediate
No. Spring rolls back by default only for RuntimeException and Error. The checked IOException propagates but the transaction commits, leaving the order marked paid.
Fix: @Transactional(rollbackFor = IOException.class) or rollbackFor = Exception.class.
⚠ Follow-up traps
Does EJB behave the same way? Yes, checked exceptions are application exceptions and do not roll back by default.
Does Error roll back? Yes in Spring.
#transactions#rollback#checked
Q67
Why does the outer method fail with UnexpectedRollbackException?
advanced
The inner call joined the outer transaction. When it threw, the proxy marked the shared transaction rollback-only. On exit, outer tries to commit and Spring throws UnexpectedRollbackException; the save is lost too.
Fix with @Transactional(propagation = REQUIRES_NEW) on inner, or noRollbackFor, or make inner not throw.
⚠ Follow-up traps
Is the save committed? No, whole transaction rolls back.
Does NESTED help? Yes with JDBC savepoints, if the transaction manager supports them (not with default JPA).
#transactions#rollback-only
Q68
Why is rollback not happening here?
advanced
place calls save on this, bypassing the Spring proxy, so no transaction is started and no rollback rule applies. With Spring Data's own repository transaction, repo.save has already committed.
Fix: move save to another bean, put @Transactional on place, or use AspectJ weaving.
⚠ Follow-up traps
Does the exception still propagate? Yes.
Would it work if save were private? No, private methods are not advised.
#transactions#proxy#self-invocation
Q69
Review: every layer logs and rethrows. What is wrong?
basic
One failure produces three or more large stack traces, confusing triage and inflating logs. Log exactly once where the exception is finally handled (typically the global handler) and translate with the cause elsewhere.
Lower layers add context via the message and cause, not by logging.
⚠ Follow-up traps
Is it ever fine to log and rethrow? At a boundary that adds value (for example metrics plus context), at debug level.
Does the cause chain preserve the original trace? Yes.
#logging#antipattern
Q70
A REST endpoint returns 500 with StackOverflowError for two JPA entities. Why?
intermediate
Jackson serializes Author.books, each Book.author points back to the author, repeating forever until the stack overflows. Lombok toString/hashCode on both sides cause the same effect.
Return DTOs, or use @JsonManagedReference / @JsonBackReference, @JsonIgnore, or @JsonIdentityInfo.
Exclude the back-reference from @ToString and @EqualsAndHashCode.
⚠ Follow-up traps
Does lazy loading prevent it? No, serialization triggers loading.
Is increasing -Xss a fix? No, the recursion is infinite.
#stackoverflowerror#jpa#serialization
Q71
"OutOfMemoryError: unable to create native thread". What do you check?
advanced
The JVM did not hit heap limits; the OS refused a new thread. Typical causes: unbounded thread creation (newCachedThreadPool, one thread per task), low ulimit -u, container pid limit, or too little native memory with a large -Xss.
Take a thread dump to see who creates threads.
Bound pools, use virtual threads (Java 21) for blocking I/O, reduce -Xss.
Raising -Xmx makes it worse by leaving less native memory.
⚠ Follow-up traps
Does a heap dump help? Not much; use a thread dump and OS limits.
Can this occur with plenty of free heap? Yes, exactly this case.
#oom#threads#production
Q72
A hot loop uses exceptions to detect invalid input and is slow. Why and what to do?
advanced
Each failure allocates an exception and fills its stack trace, which is costly especially under deep framework stacks. If most inputs are invalid, throughput drops by orders of magnitude versus a simple check.
Pre-validate with a character loop or regex, or use a parser returning Optional/OptionalInt.
When exceptions are necessary and frequent, create stackless ones (super(msg, null, false, false)).
⚠ Follow-up traps
Is the try block slow when input is valid? No, no overhead on the happy path.
Is it a problem if invalid input is rare? No, readability matters more.
#performance#stacktrace#control-flow
Q73
Production logs show a NullPointerException with no stack trace. Why?
advanced
After a hot code path throws the same implicit exception (NPE, ArithmeticException, ArrayIndexOutOfBounds, ClassCastException, ArrayStoreException) many times, C2 compiles the throw to reuse a preallocated exception without a stack trace (OmitStackTraceInFastThrow).
Search earlier logs for the same exception, where the trace was still present.
Disable with -XX:-OmitStackTraceInFastThrow.
Fix the actual bug throwing so frequently.
⚠ Follow-up traps
Does it apply to exceptions created with new? No, only JIT-intrinsified implicit ones.
Does it affect correctness? No, only diagnostics.
#npe#jit#stacktrace
Q74
A worker loop swallows InterruptedException and the service will not shut down. Why?
intermediate
shutdownNow() interrupts the thread; take() throws and clears the flag; the catch ignores it and the loop continues forever. Restore the flag and exit.
Does shutdown() interrupt workers? No, only shutdownNow().
Does take() leave the flag set? No, it throws and clears it.
#interrupted#shutdown#threads
Q75
Review this custom exception design.
intermediate
Problems: single generic type (callers cannot react differently), checked (forces boilerplate through every layer), no cause constructor, no error code, vague message.
Prefer a small hierarchy of unchecked exceptions (NotFoundException, ConflictException, ValidationException) deriving from one base with an error code.
Add (String, Throwable) constructor and include relevant identifiers in the message.
⚠ Follow-up traps
Should the message be user-facing? No; map codes to localized messages at the API edge.
Add a serialVersionUID? Yes for stable serialization if exceptions cross JVMs (RMI, caches).
#custom-exception#design#review
Q76
The API returns the raw exception message on 500. Is that a problem?
basic
Yes. Messages can leak SQL, table names, file paths, library versions or personal data, helping attackers. Return a generic message plus a correlation/trace id, and log the full details server side.
Boot's default server.error.include-stacktrace=never and include-message=never exist for this reason.
Validation errors are fine to detail because they describe client input.
⚠ Follow-up traps
How does support find the cause? Correlation id in the response and logs (MDC).
Is hiding the message security by obscurity? It is information-leak reduction, not the only control.
#rest#security#error-handling
Q77
Which handler runs when several `@ExceptionHandler` methods match?
intermediate
The handler whose declared type is closest in the exception's class hierarchy wins, so nf handles OrderNotFoundException. Method order does not matter.
A handler in the controller itself overrides global @ControllerAdvice handlers, even a less specific one.
Multiple advice beans are ordered with @Order.
⚠ Follow-up traps
Two handlers for the same type in one class? Startup fails with an ambiguity error.
Are exceptions wrapped in ServletException matched by cause? Spring also checks the cause chain.
#spring#exceptionhandler
Q78
What does this CompletableFuture print?
intermediate
CompletionException:x. thenApply is skipped; the exception reaches exceptionally wrapped in a CompletionException since it passed through a dependent stage.
If exceptionally were attached directly to supplyAsync, the argument is the raw IllegalStateException in many cases, but code should not depend on it; unwrap defensively.
The output may appear after main ends, since it is async on the common pool (daemon threads).
⚠ Follow-up traps
Does thenAccept run? Yes, the recovered value feeds it.
Does get() throw CompletionException? No, ExecutionException; join() throws CompletionException.
#completablefuture#tricky-output
Q79
A parallel stream throws from several elements. What does the caller see?
advanced
One exception propagates from the terminal operation; which one is not deterministic. Other tasks may have run partially, and other exceptions are typically discarded (the fork-join framework may attach them).
Do not rely on cleanup or ordering; collect errors explicitly (for example into a concurrent queue) when you need them all.
⚠ Follow-up traps
Is it always the first element's exception? No.
Does it stop other elements? Cancellation is best-effort; some still run.
#streams#parallel#exception
Q80
Does `finally` run here?
basic
Only try is printed. System.exit halts the JVM (after shutdown hooks), so finally never runs.
Same for Runtime.halt, a JVM crash, kill -9, or an infinite loop in try.
⚠ Follow-up traps
Do shutdown hooks run? Yes for exit, not for halt.
If the thread is a daemon and main ends mid-try? The JVM may exit without running finally.
#finally#system-exit
Q81
What happens when the catch block throws and a finally exists?
intermediate
finally is printed, then RuntimeException: from catch propagates. The original IOException is not attached unless passed as a cause: new RuntimeException("from catch", e).
⚠ Follow-up traps
Is the IOException visible in the trace? Not without the cause.
Does a sibling catch clause handle the exception thrown from this catch? No, only enclosing try statements do.
#catch#finally#tricky-output
Q82
What is the output when both catch and finally have effects?
basic
finally catch. The return value is computed, finally prints, then the method returns and println outputs the saved value.
⚠ Follow-up traps
What if finally modifies a returned StringBuilder? The caller sees the modified object.
What if the string were modified in finally? Strings are immutable, so the returned value is unchanged.
#catch#finally#return#tricky-output
Q83
Does this compile?
intermediate
No. An override cannot declare a checked exception broader than the overridden method; Exception is broader than IOException. throws FileNotFoundException, no throws, or throws RuntimeException would be fine.
⚠ Follow-up traps
Does it matter if it was Error? Unchecked types are allowed.
Why the rule? Callers using A a = new B() only prepared for IOException.
#overriding#throws#compile-error
Q84
Does this precise-rethrow code compile?
advanced
Yes (Java 7+). The compiler knows the try body can throw only IOException (plus unchecked), and e is effectively final, so throw e is treated as throwing IOException.
If you assign e = new Exception(); before throwing, it no longer compiles without throws Exception.
⚠ Follow-up traps
What if you wrap: throw new Exception(e)? Then throws Exception is required.
Does it change at runtime? No, only compile-time analysis.
#rethrow#compile
Q85
Which of these multi-catch blocks compile?
intermediate
Only (1) compiles. (2) fails: alternatives are subclass-related. (3) fails because the multi-catch parameter is implicitly final.
In (1) e has the static type Exception, so only members common to the alternatives are accessible without a cast.
⚠ Follow-up traps
Can a single-type catch parameter be reassigned? Yes.
Does the order of alternatives matter? No.
#multi-catch#compile
Q86
What happens with a null resource?
intermediate
Prints in with no exception: the generated code checks r != null before calling close().
⚠ Follow-up traps
Is a null resource allowed in the initializer expression? Yes, only the call on null would fail later.
Does the null check apply to older finally-style code? No, you would write it.
#try-with-resources#null
Q87
What if the second resource fails during initialization?
intermediate
open aclose acaught init
Resources already initialized are closed when a later one fails; the body never runs. The catch runs after the close.
⚠ Follow-up traps
Is b closed? No, it never existed.
Would the same be true with nested try/finally written by hand? Only if written carefully.
#try-with-resources#initialization#tricky-output
Q88
What does each line produce?
basic
5 / 0 throws ArithmeticException: / by zero (execution stops). If it were skipped: 5.0 / 0 prints Infinity, 5 % 0 throws ArithmeticException, 0.0 / 0 prints NaN.
Floating-point division never throws; integer division and remainder by zero do.
⚠ Follow-up traps
What does Integer.MIN_VALUE / -1 do? Overflows to MIN_VALUE, no exception.
What about Math.floorMod(5, 0)?ArithmeticException.
#arithmetic#tricky-output
Q89
When does removing inside a for-each loop throw?
intermediate
Prints [a, c] without exception. Removing the second-to-last element leaves size == cursor, so hasNext() returns false and next() (which checks modCount) is never called. Removing "a" or "c" would throw ConcurrentModificationException.
Use Iterator.remove() or removeIf.
⚠ Follow-up traps
Is CME guaranteed on concurrent modification? No, fail-fast is best effort.
Is CME only for multithreading? No, single-thread structural modification triggers it too.
#concurrentmodificationexception#tricky-output
Q90
What does `Optional.orElse` do with a throwing argument?
intermediate
It throws IllegalStateException. orElse takes a value, so fail() is evaluated eagerly before the call even though the Optional is present. Use orElseGet(() -> fail()) or orElseThrow.
⚠ Follow-up traps
Does orElseGet evaluate when present? No, the supplier is lazy.
Does Optional.of(null) throw? Yes, NPE; use ofNullable.
#optional#eager-evaluation#tricky-output
Q91
What exceptions do immutable collection factories throw?
basic
l.add(3) throws UnsupportedOperationException. Map.of("k", null) throws NullPointerException; the Java 9 factory methods reject nulls (List.of, Set.of, Map.of) and contains(null) also throws NPE.
Arrays.asList supports set but not add/remove (also UnsupportedOperationException).
⚠ Follow-up traps
Does Collections.unmodifiableList accept nulls? Yes.
Does List.copyOf accept null elements? No.
#collections#unsupported-operation
Q92
An exception in an `@Async void` method disappears. Why?
advanced
The call runs on another thread, so the caller cannot catch it. For void methods Spring passes the exception to an AsyncUncaughtExceptionHandler (by default it just logs at error level via SimpleAsyncUncaughtExceptionHandler).
Return CompletableFuture<Void> to propagate failures to the caller.
Configure a custom handler through AsyncConfigurer.getAsyncUncaughtExceptionHandler().
⚠ Follow-up traps
Does a transactional context from the caller carry over? No, the async thread has its own.
Would try/catch around the call catch it? No.
#spring#async#uncaught
Q93
A stream is created but the lambda would throw. Does it?
basic
Prints created and nothing is thrown: intermediate operations are lazy and only run when a terminal operation executes. s.count() on Java 9+ may even skip map when size is computable, so no exception either; s.toList() would throw.
⚠ Follow-up traps
Does count() always run map? Not when the size is known and no filter alters it (Java 9+).
Can you reuse s after a terminal op? No, IllegalStateException.
#streams#lazy
Q94
Original cause is lost after wrapping. How to spot and fix?
basic
The new exception has no cause, so the original stack trace and type disappear. Pass it along: throw new ServiceException("Failed to read config", e);.
Also avoid throw new RuntimeException(e.toString()) and e.printStackTrace().
⚠ Follow-up traps
If the original message is null? The new message is the string "null", which makes it worse.
Does initCause help if the class lacks a cause constructor? Yes, once.
#cause#wrapping#antipattern
Q95
Connection pool exhausted after a few failures. What is the likely bug?
intermediate
When executeUpdate throws, close() calls are skipped and the connection is never returned to the pool. After a few failures the pool is exhausted.
Does Connection.close() really close with a pool? It returns it to the pool.
Do you need to close the ResultSet separately? Closing the statement closes it, but explicit try-with-resources is clearer.
#resources#try-with-resources#leak
Q96
An exception in a constructor: what state does the caller have?
intermediate
Prints null. The object was allocated but the constructor did not complete, so the assignment never happens. If the constructor opened resources before failing, it must release them itself, as no one holds the reference to close.
Partially constructed objects can leak this (for example registering listeners) before failing; avoid it.
⚠ Follow-up traps
Is the half-built object eligible for GC? Yes unless this escaped.
Can a subclass constructor catch the super constructor's exception? No, super(...) must be first and cannot be wrapped.
#constructor#exception
Q97
A retry loop around a transactional call keeps failing. Why?
advanced
After a persistence exception the transaction is typically rollback-only and the JPA session may be inconsistent, so retries in the same transaction fail or end with UnexpectedRollbackException. Retry must wrap the whole transactional unit, outside the proxy.
Use Spring Retry with @Retryable on a bean that calls the transactional bean (aspect order: retry outside transaction).
Retry only transient errors (deadlock, optimistic lock).
⚠ Follow-up traps
Is the entity safe to reuse after the failure? It may be detached or in an unknown state; reload it.
Which exceptions are retryable?CannotAcquireLockException, OptimisticLockingFailureException, not constraint violations.
#retry#transactions#design
Q98
How would you design the global error contract for a REST API?
advanced
Use one consistent body for all failures (RFC 9457 ProblemDetail), stable machine-readable error codes, a correlation id, and mappings from exception types to statuses in one @RestControllerAdvice.
{ "type": "https://api.example.com/errors/order-not-found", "title": "Order not found", "status": 404, "detail": "Order 42 does not exist", "code": "ORDER_NOT_FOUND", "traceId": "9f1c2a"}
Validation errors carry a list of {field, message}.
Log 4xx at info/warn without stack traces, 5xx at error with stack traces.
Do not leak internals; never change codes across versions without versioning.
⚠ Follow-up traps
Should the client parse detail? No, it is for humans; use code.
Why differentiate 4xx and 5xx logging? Client errors are expected noise, server errors need alerts.
#rest#design#error-contract
Q99
Audit logging must survive a failed business transaction. How?
advanced
audit.record joined the same transaction, so rollback erases the audit row. Annotate the audit method with @Transactional(propagation = REQUIRES_NEW) on a separate bean so it commits independently.
Needs a second connection; watch pool size under load.
Alternative: write audit events outside the DB (log, message queue) or use a transactional outbox.
⚠ Follow-up traps
Does it work if audit is a method of the same class? No, self-invocation bypasses the proxy.
What if the audit commit itself fails? Decide whether failure should abort the business operation; usually log and continue.
#transactions#requires-new#audit
Q100
Why does `future.get()` throw a different exception than the one the task threw?
basic
get() wraps the task's exception in ExecutionException; the real one is getCause(), so this prints java.lang.NumberFormatException: For input string: "x".
get() also throws InterruptedException (waiting thread interrupted) and CancellationException (task cancelled); get(timeout, unit) adds TimeoutException.
⚠ Follow-up traps
Does a timeout cancel the task? No, call cancel(true) yourself.
Does ExecutionException mean get itself failed? No, the task failed.
#future#executionexception
Q101
Does the retry catch `InterruptedException` correctly?
intermediate
catch (Exception) also catches InterruptedException from call() and treats it as a retryable failure, ignoring cancellation. Also the original failure is lost in the final exception.
Catch InterruptedException separately, restore the flag and stop.
Keep the last exception as cause and earlier ones suppressed.
⚠ Follow-up traps
Does Thread.sleep inside catch compile without throws? No, it throws a checked InterruptedException.
Why backoff and jitter? To avoid retry storms on a recovering dependency.
#retry#interrupted#review
Q102
What does this print: exception thrown from a `finally` inside try-with-resources?
advanced
body close finally . Resources are closed before the optional catch and finally of the same statement.
⚠ Follow-up traps
If body threw, would finally still be last? Yes: body, close, catch, finally.
Is the resource accessible in finally? No, out of scope.
#try-with-resources#finally#tricky-output
Q103
Why can catching `Throwable` in a task loop be dangerous?
advanced
It also catches OutOfMemoryError, StackOverflowError, LinkageError and InterruptedException. The loop may spin on an unrecoverable condition, log noisily or fail again while allocating, and ignore shutdown interrupts.
Catch Exception, handle InterruptedException separately, and let VirtualMachineError terminate the process (use ExitOnOutOfMemoryError).
⚠ Follow-up traps
Is logging after OOM reliable? Not necessarily; it may need to allocate.
Does catch (Exception) suffice to keep the loop alive for bugs? Yes for normal failures.