Understanding Exception Handling
1. Understanding Exception Hierarchy
Exception hierarchy
Throwable
├── Error (unrecoverable: OOM, StackOverflow)
└── Exception (checked)
├── IOException
├── SQLException
└── RuntimeException (unchecked)
├── NullPointerException
├── IllegalArgumentException
├── IndexOutOfBoundsException
└── ClassCastException
| Type | Catch? | Declare? |
| Error | Don't | No |
| Checked Exception | Yes | Yes |
| RuntimeException | Optional | No |
2. Using try-catch Blocks
Example: try-catch
try {
int n = Integer.parseInt(input);
} catch (NumberFormatException e) {
log.error("Invalid number: {}", input, e);
return -1;
}
| Element | Detail |
try | Code that may throw |
catch | Handle specific exception type |
| Multiple catches | Most specific first |
3. Catching Multiple Exceptions
Example: Multi-catch
try {
process(file);
} catch (IOException | SQLException e) { // Java 7+
log.error("Failure", e);
throw new ServiceException(e);
}
| Pipe (|) Multi-catch | Detail |
Variable e | Implicitly final |
| Type | Common supertype |
| Exclusion | Types must not be in same hierarchy |
4. Using finally Block
| Aspect | Detail |
| Always runs | After try/catch (success or failure) |
| Skipped only when | System.exit() or JVM crash |
| Modern alternative | try-with-resources for cleanup |
Warning: Avoid return in finally — it overrides try/catch returns.
5. Using try-with-resources
Example: Auto-close
try (BufferedReader r = Files.newBufferedReader(path);
OutputStream out = Files.newOutputStream(target)) {
r.lines().forEach(line -> write(out, line));
} catch (IOException e) {
log.error("IO error", e);
}
| Aspect | Detail |
| Requires | Resource implements AutoCloseable |
| Close order | Reverse of declaration |
| Effectively-final | Pre-declared resource (Java 9+) allowed |
6. Throwing Exceptions
| Form | Example |
| Built-in | throw new IllegalStateException("msg"); |
| With cause | throw new ServiceException("...", cause); |
| Rethrow | throw e; |
7. Declaring Exceptions
| Aspect | Detail |
throws clause | Required for checked exceptions |
| Multiple | Comma-separated |
| Override rule | Subclass cannot add new checked exceptions |
8. Creating Custom Exceptions
Example: Custom exception
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; }
}
| Choice | Use |
Extend RuntimeException | Most app-level errors |
Extend Exception | Recoverable, force caller to handle |
| Constructors | Provide message + cause overloads |
9. Understanding Checked vs Unchecked Exceptions
| Aspect | Checked | Unchecked |
| Compile-time check | Yes (must catch/declare) | No |
| Base class | Exception (not RuntimeException) | RuntimeException |
| Use case | Recoverable I/O, parsing | Programming errors, invariant violations |
10. Rethrowing Exceptions
Example: Wrap and rethrow
try {
parse(data);
} catch (ParseException e) {
throw new DataException("Failed to parse", e); // chain cause
}
| Pattern | Use |
| Wrap | Translate to higher-level exception |
| Plain rethrow | Preserve original (Java 7+ precise rethrow) |
11. Understanding Suppressed Exceptions
| Concept | Detail |
| Source | try-with-resources close failures |
| Access | e.getSuppressed() returns array |
| Manual add | e.addSuppressed(other) |
12. Using Exception Chaining
| Method | Use |
| Constructor cause | new X(msg, cause) |
initCause(c) | Set cause after creation |
getCause() | Retrieve original |
13. Understanding Best Practices
| Practice | Detail |
| Catch specific | Avoid catching Exception or Throwable |
| Don't swallow | Always log or rethrow |
| Preserve cause | Pass original as cause |
| Fail fast | Validate inputs at boundaries |
| Resource cleanup | Use try-with-resources |
| Don't use exceptions for control flow | Performance + clarity |