- The Core Rule: Exceptions are for exceptional circumstances only. Never use them for ordinary control flow (e.g., terminating a loop by catching
ArrayIndexOutOfBoundsException). - Why?:
- Performance: Exception-based control flow is much slower and inhibits JVM optimizations.
- Reliability: It can mask unrelated bugs elsewhere in the system.
- API Design (State-Testing): A class with a "state-dependent" method should provide a "state-testing" method.
- Example:
Iteratorhasnext()(dependent) andhasNext()(testing).
- Example:
- Alternatives: If state-testing is impossible (e.g., in concurrent settings), have the method return an
Optionalor a distinguished value likenull.
Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
- The Three Types:
- Checked Exceptions: Use for conditions from which the caller can reasonably recover.
- Runtime Exceptions: Use to indicate programming errors (precondition violations).
- Errors: Reserved for the JVM. Do not implement new
Errorsubclasses.
- Rule of Thumb: If it's the client's fault (violated the contract), use a Runtime Exception. If it's a valid but failed operation (e.g., network timeout), use a Checked Exception.
- Exception as Object: Checked exceptions should provide methods/accessors to help the caller recover (e.g., a
getShortfall()method for insufficient funds). Never force the caller to parse the exception's string representation. - When in Doubt: Use an unchecked (runtime) exception.
- The Burden: Checked exceptions force callers to use
try-catchor propagate, which is especially painful in Streams (Item 45). - Justification Test: Only use checked exceptions if:
- The condition cannot be prevented by proper API use.
- The caller can take useful action to recover.
- Refactoring to Unchecked:
- Optional: Return an
Optional<T>instead of throwing. (Pros: Cleaner; Cons: No detailed error info). - State-Testing Method: Break the method into a boolean "permitted" check and an unchecked "action" (e.g.,
hasNext()/next()).
- Optional: Return an
- Summary: Use unchecked exceptions by default; use checked only when recovery is expected.
- The Core Value: Makes your API easier to learn, more readable, and reduces memory footprint.
- The Standard Toolkit:
IllegalArgumentException: Non-null parameter value is inappropriate.IllegalStateException: Object state is inappropriate for method invocation.NullPointerException: Parameter value is null where prohibited.IndexOutOfBoundsException: Index parameter value is out of range.ConcurrentModificationException: Concurrent modification detected where prohibited.UnsupportedOperationException: Object does not support the attempted method.
- Expert Selection Rule: If choosing between
IllegalArgumentExceptionandIllegalStateException:- Throw
IllegalStateExceptionif no argument values would have worked given the current state. - Throw
IllegalArgumentExceptionif some other value would have worked.
- Throw
- Rule: Do not throw
Exception,RuntimeException, orThrowabledirectly; treat them as abstract.
- Exception Translation: Higher layers should catch lower-level exceptions and throw exceptions that match the higher-level abstraction. This prevents implementation details from leaking into the API.
try { ... } catch (LowerLevelException e) { throw new HigherLevelException(...); }
- Exception Chaining: If the lower-level exception is useful for debugging, use chaining by passing the cause to the higher-level exception's constructor.
try { ... } catch (LowerLevelException cause) { throw new HigherLevelException(cause); }
- Preemption: Where possible, avoid exceptions from lower layers by checking parameters beforehand in the higher layer.
- The Javadoc Rule: Use the
@throwstag for every exception a method can throw, whether checked or unchecked. - Signature Rule:
- Checked: Declare individually in the method's
throwsclause. - Unchecked: Do NOT use the
throwskeyword in the method signature; let the Javadoc handle it. This provides a visual cue that the exception is unchecked.
- Checked: Declare individually in the method's
- No Shortcuts: Never throw
ExceptionorThrowabledirectly in public methods (except formain). - Class-Level Docs: If an exception (like
NullPointerException) is thrown by all methods for the same reason, document it once in the class-level Javadoc.
- Rule: Detail messages should contain the values of all parameters and fields that contributed to the exception (e.g.,
IndexOutOfBoundsExceptionshould include the index and the actual bounds). - Security: Never include passwords, encryption keys, or sensitive data in exception messages.
- Advanced Pattern: Rich Exception Constructors: Design exception classes to take the failure data in their constructor rather than just a string message.
- Example:
public MyException(int value) { super("Invalid: " + value); this.value = value; }
- Example:
- Accessors: Provide getters for the failure-capture information so callers can recover programmatically (Item 70).
- The Core Rule: A failed method invocation should leave the object in the state it was in prior to the invocation. This is essential for recoverable (checked) exceptions.
- Techniques to achieve it:
- Immutability: Free failure atomicity (Item 17).
- Check Parameters First: Perform all validity checks (Item 49) before modifying the object's state.
- Order of Computation: Arrange the code so that parts that may fail occur before parts that modify state.
- Temporary Copy: Perform the operation on a temporary copy of the object and replace the original contents only if the operation succeeds.
- Documentation: If a method cannot be made failure-atomic, the API documentation must clearly describe the state the object will be left in.
- The Core Warning: An empty
catchblock is a "fire alarm turned off." It leads to silent failures and makes debugging impossible. - Legitimate Exceptions: Occasionally, it is okay to ignore an exception (e.g., closing a stream where you've already read all data).
- Mandatory Requirements: If you MUST ignore an exception:
- The catch block must contain a comment explaining why it is appropriate.
- The exception variable must be named
ignored.
try { numColors = f.get(1L, TimeUnit.SECONDS); } catch (TimeoutException ignored) { // Use default: minimal coloring is desirable, not required }