Exceptions
7 pages · ~4 min✓ Reviewed
Builds on Collections Framework. Next up: JVM and Memory.
Exception Mental Model
1. Throwable Hierarchy
- Error = JVM failures like
OutOfMemoryError; do not catch - Everything under
ExceptionexceptRuntimeExceptionis checked
2. Checked vs Unchecked
| Checked | Unchecked | |
|---|---|---|
| Extends | Exception, not RuntimeException | RuntimeException or Error |
| Compiler | forces catch or declare | no enforcement |
| Examples | IOException, SQLException | NullPointerException, IllegalArgumentException |
| Use for | recoverable conditions | programming bugs |
3. Common Mistake
- Catching
ThrowableorExceptionswallows Errors and bugs - Catch the most specific type
Rule of thumb
Caller can recover → checked. Caller cannot fix it, it is a bug → unchecked.
try, catch and finally Flow
1. Syntax and Catch Order
tryneeds at least onecatchorfinally- Catch blocks are checked top to bottom: first matching type wins
- Subclass before superclass, or compile error
java
try { read(); } catch (FileNotFoundException e) { } catch (IOException e) { } finally { cleanup(); }
subclass first, superclass after
2. Multi-catch and finally
catch (IOException | SQLException e)handles unrelated typeseis implicitly final- Types cannot be related by inheritance
- tryrisky code
- catchonly if matching
- finallyalways runs
finallyruns with or without an exception, even afterreturn- Skipped only by
System.exit()or a JVM crash
3. Common Mistake
No return or throw in finally
It overrides the try result and silently discards the original exception.
try-with-resources
1. Syntax and Close Order
- Auto-closes any AutoCloseable declared in
try (...) - Closes in reverse order of declaration
- Closing happens before any
catchorfinally
java
try (BufferedReader br = new BufferedReader(new FileReader(f))) { return br.readLine(); } catch (IOException e) { handle(e); }
- Body
- close()reverse order
- catch
- finally
2. Suppressed and Java 9
- Body and
close()both throw: body exception is primary - Close exception attached via
getSuppressed() - Java 9+: effectively final variable works:
try (res) { ... }
3. Mistake and Interview Point
- Manual
finally { close() }: verbose, and a failingclose()can hide the original exception
- What is a suppressed exception? A close() error kept on the primary one
- Close order? Reverse of declaration, before catch
- Why prefer it over finally? Shorter, never loses the original error
throw vs throws and Custom Exceptions
1. throw vs throws
| throw | throws | |
|---|---|---|
| Is a | statement | signature clause |
| Takes | one exception object | exception types (required for checked) |
| Where | method body | after the parameter list |
| Example | throw new IllegalStateException("msg") | void read() throws IOException |
2. Custom Exceptions
- Checked:
class AppException extends Exception - Unchecked:
extends RuntimeException - Provide (message) and (message, cause) constructors
- Wrap lower-level errors, keep the cause
java
class AppException extends Exception { AppException(String m, Throwable c) { super(m, c); } } try { load(); } catch (IOException e) { throw new AppException("load failed", e); }
wrap and rethrow
3. Overriding Rules
- Override cannot throw a broader checked exception than the parent
- It may throw fewer, narrower, or any unchecked exception
- Parent
throws IOException, childthrows Exception→ compile error
Keep the cause
Always pass e when wrapping, or the original stack trace is lost.
Common Runtime Exceptions
1. Causes and Guards
| Exception | Cause | Guard |
|---|---|---|
NullPointerException | method or field on null | null check, Objects.requireNonNull, Optional |
ArrayIndexOutOfBoundsException (is an IndexOutOfBoundsException) | index < 0 or >= length | valid range 0 to length-1 |
ClassCastException | invalid downcast | instanceof before cast |
NumberFormatException (is an IllegalArgumentException) | Integer.parseInt("abc") | validate input |
ArithmeticException | int divide by zero | check divisor |
ConcurrentModificationException | remove inside for-each | Iterator.remove(), removeIf |
2. Safe Removal
- Use removeIf instead of
list.removeinside a for-each loop
java
List<Integer> l = new ArrayList<>(List.of(1, 2, 3)); l.removeIf(n -> n == 2); System.out.println(l);
output
[1, 3]
3. Remember
Int vs double
5 / 0 throws ArithmeticException, but 5.0 / 0 gives Infinity.
- Interview: why is
5.0 / 0no exception? Floating point defines Infinity; only int division throws. - Interview: how to remove while looping?
Iterator.remove()orremoveIf.
Exceptions Cheat Sheet
1. Hierarchy
- 1Throwableroot
- 2ErrorJVM, unchecked
- 3Exceptionchecked
- 4RuntimeExceptionunchecked
2. Order and keywords
- try
- Resources close
- Matching catch
- finally
| Keyword | Does |
|---|---|
throw | raises an object |
throws | declares in signature |
finally | always cleans up |
try(...) | auto-closes |
3. Best practice
- Catch specific types
- Never swallow with an empty catch
- Wrap with the cause:
new AppException(msg, e)
Mistake
return or throw in finally discards the original exception.
4. Interview Point
- Checked vs unchecked? Compiler forces catch or declare vs no enforcement
- finally after return? Yes; skipped only by
System.exit() - Suppressed exception? close() error attached via
getSuppressed() - Broader throws on override? No; fewer or unchecked only
Check yourself
What does this print?
F, then1finallyruns before the method returns, soFprints first
static int f() {
try { return 1; }
finally { System.out.println("F"); }
}
System.out.println(f());Why does this not compile?
Exceptionalready catchesIOException, so the second catch is unreachable- Put the subclass first
try { read(); }
catch (Exception e) { }
catch (IOException e) { }In try-with-resources, the body throws A and close() throws B. Which does the caller see?
- A, with B attached through
getSuppressed() - A manual
finally { close() }would throw B and lose A