skip to content

Best Practices

The habits that make exception handling useful: catch narrowly, never swallow, translate at layer boundaries, fail fast, close resources, and preserve traces. Interviewers often just show you bad handling code and wait.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

19

What is an empty catch block, and why is swallowing an exception considered a bug?

level: juniorimportance: must knowfreq 70%

answer

  1. Empty catch = lost signal
  2. Log with stack trace OR rethrow OR recover
  3. Symptom appears far from cause
  4. Pass the exception object, not getMessage()
  5. Intentional ignore must be explicit + commented

basics

~20 s

An empty catch block catches an exception but does nothing with it, so the error silently disappears. The program keeps running as if nothing went wrong, hiding real failures and making bugs very hard to find.

solid answer

~40 s

Swallowing means catching an exception and then doing nothing: an empty `catch` block (or one that only has a comment). The problem is that an exception signals that something went wrong, and discarding it loses that signal completely. The program continues with possibly corrupt or missing data, and the eventual failure shows up far from its real cause with no stack trace to trace it. At minimum you should log the exception with its stack trace, or rethrow it (possibly wrapped). If you truly intend to ignore it, that intent must be explicit and justified, ideally with a comment explaining why it is safe to ignore. Silent swallowing is one of the most common sources of mysterious production bugs.

go deeper

for a junior

Knows an empty catch hides errors and that you should at least log the exception with its stack trace.

for a middle

Distinguishes handle/log/rethrow, preserves the stack trace (passes the exception, not getMessage()), and marks intentional ignores explicitly.

for a senior

Articulates the 'proceed on bad state' and 'symptom far from cause' failure modes, and sets team conventions/lint rules to forbid silent swallowing.

for a principal

Drives an org-wide error-handling strategy: observability (structured logging, alerting on swallowed-error metrics), static-analysis gates, and a fail-fast vs. degrade-gracefully policy per boundary.

## What an exception is In Java, an **exception** is an object that represents an error or unusual condition that interrupts the normal flow of a program. When something goes wrong (a file is missing, a number can't be parsed, a network call fails), Java *throws* an exception: it stops the current code and looks up the call stack for code that can handle it. You handle exceptions with a **try/catch** block: ```java try { riskyOperation(); } catch (IOException e) { // handle it } ``` The `try` block holds code that might fail; the `catch` block runs only if a matching exception is thrown. ## What 'swallowing' means **Swallowing** an exception means catching it and then doing nothing useful with it. The classic form is an **empty catch block**: ```java try { riskyOperation(); } catch (Exception e) { // nothing here } ``` The exception is caught, the `catch` body runs (does nothing), and execution continues past the `try/catch` as if the operation had succeeded. The error information — what went wrong and where (the **stack trace**, the recorded chain of method calls leading to the failure) — is thrown away. ## Why this is a bug An exception is a *signal*: it tells you a precondition failed and the operation did not complete. Three things go wrong when you swallow it: 1. **The failure is hidden.** No log, no crash, no message. From the outside the program looks healthy while it is actually broken. 2. **You proceed on bad state.** If `riskyOperation()` was supposed to load data and it failed, the next lines run with missing or stale data. This can corrupt files, write wrong values to a database, or return wrong results to a user. 3. **The symptom appears far from the cause.** Because the original exception is gone, the eventual failure (a `NullPointerException` ten lines later, or a wrong number in a report next week) gives no hint of the real root cause. Debugging becomes guesswork. ## What to do instead A caught exception should always result in one of: **handle** it (take a real recovery action), **log** it (record the message *and* stack trace so it is diagnosable), or **rethrow** it (let a caller who knows more deal with it, possibly wrapped in a more meaningful exception). ```java try { riskyOperation(); } catch (IOException e) { log.error("Failed to load config", e); // keeps the stack trace throw new ConfigLoadException("config unavailable", e); // or recover } ``` Note `log.error(message, e)` passes the exception object, not `e.getMessage()` — that preserves the stack trace. ## The rare legitimate case Sometimes ignoring really is correct (e.g. a best-effort cleanup, or a parse you expect to fail). Even then, make the intent **explicit**: name the variable `ignored`, add a comment explaining *why* it is safe, and never leave a bare empty block that looks like an oversight. ```java try { socket.close(); } catch (IOException ignored) { // closing an already-broken socket; nothing useful to do } ```

  • If you must ignore an exception, how do you signal that it is intentional?
    Make the intent explicit: name the caught variable 'ignored', add a comment explaining why ignoring is safe, and prefer a narrow exception type so you are not silently hiding unrelated failures.
  • Why is e.printStackTrace() a poor choice in a server application?
    It writes to System.err, bypassing the logging framework, so it has no log level, no timestamp/context, may not be captured by log aggregation, and can be lost entirely. Use a logger and pass the exception object.

saying these in an interview costs you the question

  • Leaving a bare empty catch with no comment
  • Calling e.printStackTrace() in production and calling that 'handling'
  • Logging only e.getMessage() and dropping the stack trace
  • Believing catching an exception 'fixes' the underlying problem

context

open as a page

What is the fail-fast principle in Java, and why is validating inputs early considered good practice?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Fail-fast means you check for invalid inputs or bad state right away and stop immediately by throwing an error, instead of letting the program keep running with bad data and breaking later in a confusing place.

open as a page

What is try-with-resources in Java, and why is it preferred over closing resources in a finally block?

level: juniorimportance: must knowfreq 78%

basics

~20 s

try-with-resources declares a resource in the try header and Java automatically closes it when the block ends, even on an exception. It is preferred because manual finally blocks are easy to get wrong and can leak resources.

open as a page

You see this code in a review. What is wrong with it and how would you fix it?

level: juniorimportance: must knowfreq 60%

basics

~10 s

It throws a new exception but never passes the original (e) as the cause, so the real error and its stack trace are lost. Fix it by chaining: throw new ServiceException("...", e).

open as a page

Why should you catch a specific exception type instead of a broad Exception or Throwable?

level: middleimportance: must knowfreq 68%

basics

~20 s

Catching a specific type means you only handle the errors you expect and know how to deal with. A broad catch grabs everything — including bugs and errors you never anticipated — and treats them all the same, which hides real problems.

open as a page

Why is it considered an anti-pattern to use exceptions for ordinary control flow in Java?

level: middleimportance: must knowfreq 75%

basics

~20 s

Exceptions are meant for unusual error situations, not normal logic. Using them for expected outcomes (like 'item not found') is slow because building the error is costly, and it makes the code harder to read since the real logic hides inside try/catch.

open as a page

When catching and rethrowing an exception in Java, how do you preserve the original stack trace and cause, and what goes wrong if you don't?

level: middleimportance: must knowfreq 72%

basics

~20 s

Pass the original exception as the cause when you wrap it: throw new MyException("msg", original). If you only do throw new MyException("msg") you throw away the original's stack trace and message, so you lose where the real problem happened.

open as a page

What is exception translation in Java, and why would you do it instead of just letting a low-level exception propagate?

level: middleimportance: must knowfreq 72%

basics

~10 s

Exception translation means catching a low-level exception and throwing a new, higher-level one that fits the layer you're in. You do it so callers see errors that match your abstraction, not your internal plumbing.

open as a page

When you catch an exception you cannot fully handle, what are your options, and how do you preserve the original cause?

level: middleimportance: should knowfreq 55%

basics

~20 s

You can rethrow the same exception, or wrap it in a new, more meaningful exception. When you wrap, always pass the original as the 'cause' so the full chain and stack trace are kept and you can still see where it really started.

open as a page

How do fail-fast iterators in the Java Collections Framework work, and how do they differ from fail-safe iterators?

level: middleimportance: should knowfreq 55%

basics

~20 s

A fail-fast iterator (like ArrayList's) throws ConcurrentModificationException if the collection is changed while you iterate it, so you find the bug quickly. A fail-safe iterator (like CopyOnWriteArrayList's) works on a snapshot and never throws, but may not see the latest changes.

open as a page

What is the difference between exception translation, exception chaining, and exception masking (swallowing)?

level: middleimportance: should knowfreq 55%

basics

~20 s

Translation = throw a new, higher-level exception. Chaining = attach the original as the cause of that new exception. Masking = throw or catch in a way that loses the original. You usually translate AND chain, and never mask.

open as a page

How do you decide which exceptions to catch locally versus let propagate, and where should broad 'catch-all' handlers live in a layered application?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Catch an exception only at the level that can actually do something useful about it. Let everything else bubble up. Put one broad catch-all at the application's outer edge — like a request handler — to log any failure and return a safe response.

open as a page

What are the idiomatic Java techniques for fail-fast input validation in methods and constructors, and which exception should each case use?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Check arguments at the very top of a method or constructor before doing work: use Objects.requireNonNull for nulls, throw IllegalArgumentException for bad values, and IllegalStateException when the object isn't in a valid state for the call. This stops bad data immediately.

open as a page

Explain how a finally block can hide the real exception, and how try-with-resources fixes this with suppressed exceptions.

level: seniorimportance: should knowfreq 55%

basics

~20 s

If the try body throws one exception and the finally block throws another (for example close() fails), the finally's exception replaces the original, so the real failure is lost. try-with-resources keeps the original and attaches the close failure as a suppressed exception instead.

open as a page

A service intermittently fails with 'connection pool exhausted' under load but recovers when idle. How would you reason about and prevent this resource-leak class of bug?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Some code path borrows a connection from the pool and never returns it (no close on every path), so under load all connections get stuck and new requests block until they time out. Fix it by closing every borrowed resource with try-with-resources so it always returns to the pool.

open as a page

When translating an exception at a layer boundary, how do you decide between throwing a checked or an unchecked exception, and what should its type be?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Throw a checked exception only if the caller can realistically recover and you want to force them to handle it. For programming errors or unrecoverable failures, throw an unchecked (RuntimeException) one. Pick a type whose name and meaning fit the caller's layer.

open as a page

When designing a system, how do you decide between fail-fast and fail-safe (graceful degradation) behavior, and what are the trade-offs?

level: principalimportance: should knowfreq 40%

basics

~20 s

Use fail-fast for programmer mistakes and clearly invalid input — stop immediately so bugs surface early. Use fail-safe (keep running with a fallback or degraded mode) for expected, recoverable problems in production, like a slow external service, where staying available matters more than perfection.

open as a page

When designing your own AutoCloseable resource and a close() that can fail, what conventions should you follow so callers can clean up reliably?

level: principalimportance: nice to knowfreq 32%

basics

~20 s

Implement AutoCloseable so callers can use try-with-resources. Make close() idempotent (safe to call twice), release the underlying resource reliably, and only throw from close() if it is genuinely meaningful — never leave the resource half-open.

open as a page

When should you NOT translate an exception, and what are the costs of over-translating across many layers?

level: principalimportance: nice to knowfreq 33%

basics

~10 s

Don't translate when the existing exception is already meaningful to the caller, or when wrapping adds nothing. Over-translating creates deep 'Caused by' chains, loses specificity, and adds maintenance noise.

open as a page