💥 Exceptions & Errors · Intermediate

finally in Java

Always runs (except System.exit/JVM crash); return in finally overrides.

🧩 The mysteryA method says return 1; — yet the caller receives 2. No bug in the JVM. Just a finally block doing exactly what it was told.

The cleanup crew

A **finally block runs after the try (and any catch) no matter how it ended**: normally, with an exception, or through return, break or continue. That makes it the place for cleanup, like releasing a lock.

lock.lock();
try {
    update();
} finally {
    lock.unlock();   // always runs
}
🔮 Predict it

Return, then finally?

What does this print?

String f() {
    try {
        System.out.print("T");
        return "R";
    } finally {
        System.out.print("F");
    }
}
void main() { System.out.println(f()); }
  1. TFR
  2. TRF
  3. TR
  4. FTR
Show the answer

return "R" is paused: finally runs first and prints F, then the method returns and main prints R.

🔮 Predict it

Changing the answer late

finally changes n after return n. What prints?

int count() {
    int n = 10;
    try {
        return n;
    } finally {
        n = 20;
    }
}
void main() { System.out.println(count()); }
  1. 10
  2. 20
  3. Compile error
Show the answer

return n evaluates and saves the value (10) before finally runs. Changing n afterwards doesn't touch the saved result.

Only a dead JVM skips it

finally is skipped only when the JVM stops: **System.exit()** inside the try halts everything without returning normally, so finally never runs. A JVM crash or a killed process skips it too. Otherwise — it runs.

⚠️ The trap

return inside finally

A return in finally replaces the pending return from try — try { return 1; } finally { return 2; } returns 2. Worse, it discards an in-flight exception: below, the caller gets 7 and the error silently vanishes. Never return from finally.

int risky() {
    try {
        throw new IllegalStateException();
    } finally {
        return 7;   // exception lost!
    }
}
💼 In the real world

Locks, connections, timers

Forgetting to release a lock in finally can freeze a whole server when one request fails; forgetting to return a database connection slowly drains the pool until every request hangs. Cleanup belongs in finally (or try-with-resources, coming soon).

Key takeaways

  1. Runs after normal completion, exceptions, return, break and continue
  2. Skipped only by System.exit() or the JVM dying
  3. A return in finally replaces the pending return or exception
  4. The try's return value is computed before finally runs
🤯 Did you know?

javac can catch the dangerous pattern for you: compile with -Xlint:finally and it warns "finally clause cannot complete normally".

Practice questions

What does this print?

int test() {
    try {
        return 1;
    } finally {
        return 2;
    }
}
void main() {
    System.out.println(test());
}
  1. 2
  2. 1
  3. 1 2
  4. Compile error
Check your answer

2. The try's return 1 is pending when finally runs; finally's own return replaces it.

What does this print?

int test() {
    int x = 1;
    try {
        return x;
    } finally {
        x = 99;
    }
}
void main() {
    System.out.println(test());
}
  1. 1
  2. 99
  3. Compile error
Check your answer

1. return x evaluates x (1) and saves that value before finally runs. Changing x afterwards doesn't change the saved result.

Next: throw vs throws — one letter apart, two completely different jobs.