Skip to content

Lesson 02 · try / catch / finally

Objectives

After this lesson you will be able to:

  • Order catch blocks correctly and use multi-catch.
  • Predict finally semantics, including how it interacts with return.
  • Avoid the traps where finally swallows exceptions and overrides return values.

catch ordering and multi-catch

catch blocks are tried top to bottom; the first assignable one runs. A more specific subtype must come before its supertype, or the later block is unreachable (compile error).

java
try {
    risky();
} catch (FileNotFoundException e) {   // subclass first
    ...
} catch (IOException e) {             // superclass after
    ...
}

Multi-catch handles several unrelated types in one block with |. The exception variable is implicitly final, and the listed types may not be in a subclass relationship.

java
try {
    risky();
} catch (IOException | SQLException e) {   // one block, two types
    log(e);
}

Exam trap

In multi-catch the alternatives can't be related by inheritance — catch (IOException | FileNotFoundException e) is a compile error because FileNotFoundException is already an IOException (redundant). Also, you can't reassign e.

finally always runs

A finally block runs whether the try completes normally, throws, or even returns — it's for cleanup. The only things that skip it are System.exit() and JVM/hardware death.

java
try {
    return 1;
} finally {
    System.out.println("cleanup");   // runs BEFORE the method returns 1
}

Exam trap

A return (or throw) inside finally overrides any return/throw from the try/catch — and silently swallows a pending exception:

java
int f() {
    try { throw new RuntimeException("boom"); }
    finally { return 42; }            // exception is LOST; method returns 42
}

Never return or throw from finally. Mutating a local in finally does not change an already-evaluated return value (the value was captured when return ran).

Key Takeaways

  • catch blocks are checked in order; put subclasses before superclasses or the later block is unreachable.
  • Multi-catch (A | B) needs unrelated types; the variable is implicitly final.
  • finally runs on normal exit, exception, and return (only System.exit skips it).
  • A return/throw in finally overrides and swallows what try/catch produced — avoid it.

Lesson Quiz

Lesson Quiz · try / catch / finally0 / 5
  1. What does this method return?

    int f() {
      try { throw new RuntimeException("x"); }
      finally { return 42; }
    }
    • AThrows RuntimeException
    • B42
    • C0
    • DCompile error
  2. Why does this NOT compile?

    try { ... }
    catch (IOException | FileNotFoundException e) { }
    • AMulti-catch needs three types
    • BFileNotFoundException is a subclass of IOException (redundant)
    • Ce must be final
    • DOrder is wrong
  3. Catch order: which compiles?

    • Acatch(Exception e) then catch(IOException e)
    • Bcatch(IOException e) then catch(Exception e)
    • CBoth
    • DNeither
  4. When does a finally block NOT run?

    • AOn a thrown exception
    • BOn a return in try
    • CWhen System.exit() is called
    • DNever — it always runs
  5. What prints, and what is returned?

    try { return 1; }
    finally { System.out.println("C"); }
    • APrints C, returns 1
    • BReturns 1, no print
    • CPrints C, returns nothing
    • DCompile error

Next: try-with-resources & Custom Exceptions. Run the matching code in labs/src/main/java/com/jse21/m04_exceptions/.