Skip to content

Latest commit

 

History

History
92 lines (83 loc) · 7.12 KB

File metadata and controls

92 lines (83 loc) · 7.12 KB

Exceptions (Items 69–77) - RENUMBERED

Item 69: Use exceptions only for exceptional conditions

  • 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?:
    1. Performance: Exception-based control flow is much slower and inhibits JVM optimizations.
    2. 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: Iterator has next() (dependent) and hasNext() (testing).
  • Alternatives: If state-testing is impossible (e.g., in concurrent settings), have the method return an Optional or a distinguished value like null.

Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors

  • The Three Types:
    1. Checked Exceptions: Use for conditions from which the caller can reasonably recover.
    2. Runtime Exceptions: Use to indicate programming errors (precondition violations).
    3. Errors: Reserved for the JVM. Do not implement new Error subclasses.
  • 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.

Item 71: Avoid unnecessary use of checked exceptions

  • The Burden: Checked exceptions force callers to use try-catch or propagate, which is especially painful in Streams (Item 45).
  • Justification Test: Only use checked exceptions if:
    1. The condition cannot be prevented by proper API use.
    2. The caller can take useful action to recover.
  • Refactoring to Unchecked:
    1. Optional: Return an Optional<T> instead of throwing. (Pros: Cleaner; Cons: No detailed error info).
    2. State-Testing Method: Break the method into a boolean "permitted" check and an unchecked "action" (e.g., hasNext() / next()).
  • Summary: Use unchecked exceptions by default; use checked only when recovery is expected.

Item 72: Favor the use of standard exceptions

  • 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 IllegalArgumentException and IllegalStateException:
    • Throw IllegalStateException if no argument values would have worked given the current state.
    • Throw IllegalArgumentException if some other value would have worked.
  • Rule: Do not throw Exception, RuntimeException, or Throwable directly; treat them as abstract.

Item 73: Throw exceptions appropriate to the abstraction

  • 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.

Item 74: Document all exceptions thrown by each method

  • The Javadoc Rule: Use the @throws tag for every exception a method can throw, whether checked or unchecked.
  • Signature Rule:
    • Checked: Declare individually in the method's throws clause.
    • Unchecked: Do NOT use the throws keyword in the method signature; let the Javadoc handle it. This provides a visual cue that the exception is unchecked.
  • No Shortcuts: Never throw Exception or Throwable directly in public methods (except for main).
  • 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.

Item 75: Include failure-capture information in detail messages

  • Rule: Detail messages should contain the values of all parameters and fields that contributed to the exception (e.g., IndexOutOfBoundsException should 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; }
  • Accessors: Provide getters for the failure-capture information so callers can recover programmatically (Item 70).

Item 76: Strive for failure atomicity

  • 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:
    1. Immutability: Free failure atomicity (Item 17).
    2. Check Parameters First: Perform all validity checks (Item 49) before modifying the object's state.
    3. Order of Computation: Arrange the code so that parts that may fail occur before parts that modify state.
    4. 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.

Item 77: Don't ignore exceptions

  • The Core Warning: An empty catch block 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:
    1. The catch block must contain a comment explaining why it is appropriate.
    2. 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
    }