Why interviewers ask about exception handling
Exception handling reveals how you treat failure, and failure is where systems actually break. A candidate who swallows exceptions, logs and rethrows the same thing three times, or leaks file handles writes code that hides bugs and corrupts state under load. Interviewers use these questions to see whether you handle errors deliberately — catching what you can act on, cleaning up resources reliably, and preserving the original cause.
This page covers the exception questions that recur across levels, from the hierarchy to design-round strategy. Anchor your answers in the Throwable tree and in one principle: catch an exception only where you can do something meaningful about it.
How to answer exception questions
Start from the hierarchy — Throwable splits into Error (JVM problems you don't catch) and Exception (your domain), and Exception splits into checked and the unchecked RuntimeException branch. Then answer design questions with intent: name what you'd do in the catch block. "Log and rethrow at the same level" is a smell; "catch at the boundary, translate to a domain error, and let lower layers propagate" is a strategy.
Q1. Walk me through the exception hierarchy.
Everything throwable extends Throwable, which has two branches. Error (e.g. OutOfMemoryError, StackOverflowError) signals JVM-level problems you should not catch. Exception is your space: its RuntimeException subtree is unchecked (programming errors), and everything else under Exception is checked (recoverable conditions the compiler enforces).
Knowing the tree lets you answer follow-ups by location: NullPointerException and IllegalArgumentException are unchecked because they indicate bugs; IOException and SQLException are checked because a caller might reasonably retry or fall back.
Interview note: Trap: "is
NullPointerExceptionchecked or unchecked?" Unchecked — it extendsRuntimeException. Catching it routinely is a code smell; the fix is usually a null guard orOptional.
Q2. Checked vs unchecked — when do you use each?
Use checked exceptions for recoverable, expected failures the caller should consciously handle (a missing file, a failed remote call). Use unchecked exceptions for programming errors and precondition violations (IllegalArgumentException, IllegalStateException) that signal a bug rather than a runtime condition.
The modern debate is worth mentioning: many teams and frameworks (Spring) lean toward unchecked exceptions because pervasive checked exceptions pollute signatures and encourage swallow-and-ignore. The judgment being tested is whether you can classify a failure as "the caller can act on this" versus "someone wrote a bug."
Interview note: Follow-up: "can you make a checked exception unchecked?" You can wrap it:
catch (IOException e) { throw new UncheckedIOException(e); }, preserving the cause while removing the checked constraint.
Q3. Explain try-catch-finally and when finally does NOT run.
finally runs whether or not an exception was thrown, and even after a return in the try or catch — making it the place for cleanup. It does not run only in extreme cases: System.exit(), JVM crash, or the thread being killed.
A subtle trap: a return inside finally swallows any exception propagating out of try/catch and overrides a returned value, which is why returning from finally is discouraged.
try {
return compute();
} finally {
cleanup(); // runs before the method returns compute()'s value
}
Interview note: Trap: "if try returns 1 and finally returns 2, what is returned?" 2 —
finally's return wins and silently discards the try's value and any pending exception.
Q4. How does try-with-resources work and why is it better?
Any resource implementing AutoCloseable declared in the try(...) header is closed automatically when the block exits, in reverse order of declaration, even if an exception is thrown. It replaces error-prone manual finally cleanup and handles the multi-resource case correctly.
try (var in = new FileInputStream("a");
var out = new FileOutputStream("b")) {
in.transferTo(out);
} // out.close() then in.close(), automatically
The correctness win people miss: if the body throws and close() also throws, try-with-resources keeps the body's exception as primary and attaches the close exception as a suppressed exception (retrievable via getSuppressed()). A hand-written finally typically loses the original cause.
Interview note: Follow-up: "what interface must the resource implement?"
AutoCloseable(orCloseable, which extends it and narrowsclose()to throwIOException).
Q5. throw vs throws — what's the difference?
throw is a statement that actually raises an exception instance at runtime (throw new IllegalStateException("...")). throws is a method-signature clause declaring which checked exceptions the method may propagate, shifting the handling obligation to the caller.
They are easy to confuse by name but do opposite jobs — one produces an exception, the other advertises one. Overusing throws Exception on signatures is a red flag: it forces every caller to handle the broadest type and discards useful specificity.
Interview note: Trap: "can you throw a checked exception without declaring it?" Not normally — the compiler enforces it. The exception is generic sneaky-throws tricks, which you should mention only to say you avoid them.
Q6. How and why do you create custom exceptions?
Create a custom exception to give a failure a meaningful type and carry domain context — an order id, an error code — so callers can catch precisely and logs are useful. Extend RuntimeException for unchecked domain errors, or Exception when you want the compiler to force handling.
public class OrderNotFoundException extends RuntimeException {
private final String orderId;
public OrderNotFoundException(String orderId) {
super("Order not found: " + orderId);
this.orderId = orderId;
}
public String getOrderId() { return orderId; }
}
The interview point is restraint: create a custom exception when catch sites need to distinguish it, not one per method. A small, well-named exception hierarchy beats dozens of near-duplicates.
Interview note: Follow-up: "checked or unchecked for a custom exception?" Most modern services choose unchecked to avoid signature pollution, reserving checked for genuinely recoverable, expected conditions.
Q7. What is exception chaining and why does it matter?
Exception chaining wraps a low-level cause in a higher-level exception while preserving the original via the cause constructor argument. It lets you translate an exception to your abstraction layer without discarding the root cause and its stack trace.
try {
repository.load(id);
} catch (SQLException e) {
throw new DataAccessException("Failed loading " + id, e); // e is the cause
}
Losing the cause — throw new DataAccessException(...) with no e — is a classic bug that produces a stack trace pointing at your wrapper instead of the real failure, making production incidents much harder to diagnose.
Interview note: Trap: "how do you see the original error later?"
getCause()returns it, and printed stack traces show aCaused by:section for the full chain.
Q8. What are exception-handling anti-patterns interviewers watch for?
The big ones: an empty catch block that swallows the exception; catching Exception too broadly deep in the code; logging and rethrowing the same exception at every layer (log spam); using exceptions for normal control flow; and catching then returning null, hiding the failure from the caller.
The unifying principle is: catch where you can handle, log once at the boundary, and never make a failure silently disappear. A candidate who names these patterns and the fix — handle, translate, or propagate — demonstrates production maturity beyond syntax.
catch (IOException e) {
// anti-pattern: swallowed. The bug is now invisible.
}
Interview note: Follow-up: "where should you log an exception?" Once, at the boundary that decides the response — typically a controller advice or a top-level handler — not at every layer it passes through.
How to prepare
Build a tiny service method and route a failure through it end to end: throw a checked IOException from a repository, wrap it in a domain exception with the cause preserved, and handle it once at the boundary. Then deliberately introduce the anti-patterns — the swallowed catch, the lost cause, the return in finally — and observe how each hides information. Seeing the failure modes is what makes the "best practices" answer sound lived rather than memorized.
Pair this with the multithreading questions, where exception handling inside tasks and executors has its own traps, and the 1 year experience set for how exception basics are framed for juniors. If the hierarchy still feels shaky, rebuild it on the Java learning path before your next round.
Frequently Asked Questions
What exception topics are asked most in Java interviews?
Do freshers get asked about exception handling?
What is the difference between checked and unchecked exceptions?
Is try-with-resources always better than finally?
Should you ever catch Exception or Throwable?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — View the Java Full Stack curriculum

