Chapter 9 Flashcards — Exceptions

flashcards effective-java exceptions


When is it appropriate to use exceptions for control flow?
?
Never. Exceptions are for exceptional conditions only — conditions outside the normal flow of execution. Using exceptions for control flow (e.g., catching ArrayIndexOutOfBoundsException to terminate a loop) is slower (JVM cannot optimize exception paths like normal code), harder to read, and silently swallows real exceptions from within the loop body.


What is the Bloch decision rule for checked vs. unchecked exceptions?
?
Checked exception: use when the caller can reasonably be expected to recover from the failure. The compiler enforces handling. RuntimeException (unchecked): use when the exception indicates a programming error (precondition violation, illegal state) — the caller should fix their code, not handle the exception. Error: reserved for JVM-level failures only; never throw or catch in application code.


What is a state-testing method, and how does it relate to exceptions?
?
A state-testing method checks whether an object is in the right state to invoke a state-dependent method. Example: Iterator.hasNext() before Iterator.next(). Providing state-testing methods eliminates the need for callers to use exceptions as flow control. Alternative: return Optional or a sentinel value for “absent” rather than throwing.


What are the three types of throwables in Java, and when is each used?
?

  1. Checked exceptions (Exception subclasses, excluding RuntimeException): recoverable conditions; callers must handle or propagate. 2. Runtime exceptions (RuntimeException subclasses): programming errors; unchecked. 3. Errors (Error subclasses): JVM-level failures (OutOfMemoryError, StackOverflowError); never throw, catch only in top-level handlers for logging.

Why do checked exceptions conflict with Java streams?
?
Standard functional interfaces (Function, Consumer, Supplier, etc.) do not declare checked exceptions. This means you cannot use a method that throws a checked exception directly in a lambda or method reference without wrapping it. The workaround is to catch the checked exception inside the lambda and rethrow as UncheckedIOException or another unchecked wrapper. This friction is a known design tension in modern Java.


When should you avoid adding a checked exception to a method signature?
?
When there is nothing reasonable the caller can do — i.e., no realistic recovery path. Ask: “Can callers retry, fall back, or notify the user meaningfully?” If no, the exception should be unchecked. Also avoid checked exceptions when a method has only one checked exception and the caller typically just wraps it in RuntimeException — that is a signal to make it unchecked or return Optional instead.


Name six standard JDK exceptions and their canonical use cases.
?

  1. IllegalArgumentException — parameter value is inappropriate. 2. IllegalStateException — object state is not valid for this call. 3. NullPointerException — null passed where prohibited. 4. IndexOutOfBoundsException — index is out of valid range. 5. UnsupportedOperationException — operation not implemented/supported. 6. ConcurrentModificationException — illegal concurrent modification detected.

When is it justified to create a custom exception class instead of using a standard one?
?
When: (1) the exception carries domain-specific state that callers need programmatically (e.g., InsufficientFundsException with getRequested() and getAvailable()); (2) callers need to catch this specific type by name as part of a protocol; (3) the standard name would be misleading for the domain. Always extend the most specific applicable standard exception (e.g., extend IllegalArgumentException rather than bare RuntimeException).


What is exception translation?
?
Exception translation is the practice of catching a lower-level exception and rethrowing it as a higher-level exception that matches the current abstraction layer. Example: a repository catches SQLException and throws a domain-level RepositoryException. This keeps layers decoupled — the service layer does not need to know about SQL. Always chain the original cause: throw new HighLevelException("msg", cause) so diagnostic information is not lost.


What is exception chaining, and how do you implement it?
?
Exception chaining preserves the original exception as the cause of a translated exception. Implementation: pass the original exception to the constructor of the new exception: throw new ServiceException("Failed to load user", originalException). All standard exception constructors accept a Throwable cause parameter. The cause is accessible via e.getCause() and appears in the full stack trace when printed.


What is the most important rule when documenting exceptions?
?
Document every exception — both checked and unchecked — with @throws Javadoc. Be specific about the condition that triggers the exception (not the implementation). For unchecked exceptions that cannot appear in the throws clause, document them anyway. “Throws NullPointerException if any parameter is null” is infinitely more useful than no documentation at all.


What should an exception detail message contain?
?
All values relevant to diagnosing the failure: the actual argument values, the bounds that were violated, identifiers (account ID, resource URL, etc.). Example: “Index 42 out of bounds for length 10” not “Index out of bounds”. Do not include passwords, tokens, or PII — these end up in logs. The detail message is for developers, not end users — keep user-facing messages separate.


What is failure atomicity?
?
Failure atomicity means a method that throws an exception leaves the object in exactly the state it was in before the call. The four strategies to achieve it: (1) check all preconditions before any mutation; (2) operate on a defensive copy and swap only on success; (3) write rollback code to undo mutations on failure; (4) use immutable objects, which are naturally failure atomic because each operation returns a new object.


Is failure atomicity always achievable?
?
No. For concurrent operations, achieving failure atomicity requires synchronization or immutability. For operations with external side effects (network calls, database writes), rollback may be impossible or prohibitively expensive. In those cases, the contract must be documented clearly: specify what state the object is in after a failed call. Don’t promise failure atomicity you cannot deliver.


What is the danger of an empty catch block?
?
An empty catch block silently discards the exception signal. The program continues as if nothing went wrong, but it may be in an unknown or corrupt state. Bugs behind silent catches often surface much later in completely unrelated code, making them extremely hard to diagnose. The minimum acceptable response is a log statement. If you truly intend to ignore it, name the variable ignored and add a comment explaining why.


What is the correct way to handle InterruptedException?
?
Two options: (1) Propagate it by declaring throws InterruptedException — let the caller decide. (2) Restore the interrupt flag by calling Thread.currentThread().interrupt() after catching it, then handle the interruption (e.g., return early, set a flag). Never swallow InterruptedException silently — catching it clears the thread’s interrupt status, which can prevent graceful shutdown in thread pools.


What Java 14 JVM feature reduces the need to manually craft NPE messages?
?
JEP 358 (Helpful NullPointerExceptions): the JVM now generates descriptive NPE messages like “Cannot invoke String.length() because str is null” automatically, identifying the exact null variable. This reduces the need to manually include variable names in NPE detail messages, but explicit Objects.requireNonNull(param, "param") is still good practice for precondition documentation.


What is Objects.checkIndex(), and when was it introduced?
?
Objects.checkIndex(int index, int length) (Java 9+) throws IndexOutOfBoundsException with a descriptive message if index < 0 || index >= length. It should be preferred over manual bounds checks in collection implementations and array-based code because it generates better messages automatically and is a clear statement of intent.


What is UncheckedIOException and when should you use it?
?
UncheckedIOException (Java 8+) is a RuntimeException wrapper for IOException. Use it when you need to use a method that throws IOException inside a lambda, stream pipeline, or other context that does not permit checked exceptions. It preserves the original IOException as the cause. Pattern: catch (IOException e) { throw new UncheckedIOException(e); }.


Why is catching Exception or Throwable in catch blocks usually wrong?
?
Catching Exception catches RuntimeException too, which means programming errors (null pointer, illegal argument, class cast) are silently absorbed rather than crashing the program to reveal the bug. Catching Throwable also catches Error (OutOfMemoryError, etc.) which should generally be fatal. Both hide problems and make debugging extremely difficult. Catch the most specific exception type that you can meaningfully handle.


What distinguishes the detail message from a user-facing error message?
?
The detail message (exception’s getMessage()) is for developers and log readers. It should contain raw technical values — indices, IDs, actual vs. expected values. A user-facing error message should be translated into human-friendly language, should not expose internal implementation details, and should never contain stack traces or technical identifiers. They are separate concerns; never display raw exception messages to end users.


When should you use Optional instead of throwing a checked exception?
?
Use Optional when the absence of a value is a normal, expected outcome — not an exceptional one. Example: Optional<User> findById(long id) signals that the user might not exist as a normal case. Use a checked exception when failure represents an unexpected condition that the caller should explicitly plan for. Use an unchecked exception when the call is invalid (programming error). Optional should not be used for collections — return an empty collection instead.


What is the TOCTOU problem, and how does it relate to state-testing methods?
?
TOCTOU (Time-Of-Check/Time-Of-Use) race: in a concurrent environment, the state can change between a state-testing call (hasNext()) and the state-dependent call (next()). Another thread may modify the object between the two calls, making the state-testing result invalid. The solution: use Optional or a combined atomic operation rather than a separate check. See ch10-concurrency for synchronization strategies.


What should a custom exception class provide beyond its message?
?

  1. Accessors for all failure-relevant fields — so callers can programmatically inspect the failure (not just parse the string). 2. Constructor that passes the message to super — so the standard message format works. 3. Constructor that accepts a cause — so exception chaining works. 4. Serializable — exception classes should be serializable (all Throwable subclasses are). Do not add behavior or mutable state.

What does try-with-resources do that a manual finally block does not?
?
try-with-resources automatically calls close() on the resource when the try block exits. More importantly: if both the try block and close() throw exceptions, the exception from close() is added as a suppressed exception (accessible via e.getSuppressed()) rather than replacing the original exception. Manual finally blocks typically lose the original exception when close() throws, silently swallowing it.


Name a situation where exception translation would be wrong (too much translation).
?
When the lower-level exception IS the appropriate abstraction for the higher level. Example: a network service component that naturally deals with IOException — forcing it to translate to a domain exception adds noise without adding clarity. Translation is justified at genuine abstraction boundaries (DAO → Service, library → application). Within the same abstraction layer, letting an exception propagate is correct.


What is wrong with a method that declares throws Exception?
?
It is too vague to be useful: (1) callers must catch Exception, which also catches RuntimeException, degrading their error handling; (2) it tells callers nothing about what can fail or why, making it impossible to write targeted handlers; (3) removing it later may break callers who catch it specifically. Always declare the specific exception types, and document each with @throws Javadoc describing the triggering condition.


How do records (Java 16+) provide failure atomicity?
?
Records are immutable — all fields are set in the constructor and cannot be changed afterward. The compact constructor allows validation with early exceptions before the record is created. If the constructor throws, no record is created — the original reference is unchanged. This is the “immutable objects” strategy for failure atomicity: operations return new records or throw, never leaving a half-modified object.


Total Cards: 28
Review Time: ~30 minutes
Priority: HIGH
Last Updated: 2026-05-10