Java finally Block
Run cleanup code in Java with finally blocks that always execute, whether or not an exception was thrown.
Java finally Block
A finally block runs no matter how the try exits — normal completion, caught exception, uncaught exception, or even an early return. That guarantee is its whole purpose: it's where you put the cleanup that has to happen, period. Closing a file handle, releasing a lock, restoring a thread state — anything whose absence would leave the program in a worse condition than where it started.
The shape
try {
// risky code
} catch (SomeException e) {
// optional — zero or more catches
} finally {
// always runs after the try (and any matching catch)
}You can pair finally with catch, with no catch at all (try { ... } finally { ... }), or with multiple catches. The pieces compose freely.
What "always runs" means
A finally block runs when control leaves the try, regardless of how:
- Normal end of the
tryblock —finallyruns after the last statement. - An exception thrown from
try—finallyruns after the matchingcatchfinishes (or, if no catch matches, just before the exception propagates). returninsidetryorcatch—finallyruns before the return actually takes effect.breakorcontinuethat would exit thetry—finallyruns before the jump.
The only ways to skip finally are: the JVM itself dies (System.exit, a power cut, Runtime.halt), an infinite loop or deadlock inside the try, or Thread.stop (which is deprecated for this exact reason). For everything you write in normal application code, finally is a hard guarantee.
try {
return computeAnswer(); // even though there's a return here,
} finally {
cleanup(); // this runs before the method actually returns
}What finally is for
The honest answer is: resource cleanup, almost always. Before Java 7 introduced try-with-resources, the canonical shape was:
InputStream in = null;
try {
in = new FileInputStream(path);
// read from in...
} catch (IOException e) {
// handle
} finally {
if (in != null) {
try { in.close(); } catch (IOException ignored) { /* */ }
}
}That nested try/catch around close() in the finally is the kind of noise try-with-resources was built to delete. We'll see it in the next chapter. But understanding what finally is for makes the newer construct make sense.
Beyond resources, finally is useful for:
- Restoring shared state you mutated for the duration of the
try— incrementing a depth counter, toggling a flag, swapping aThreadLocal. - Releasing manually acquired locks (
Lock.lock()→try { ... } finally { lock.unlock(); }). - Stopping timers or closing transactions that don't implement
AutoCloseable.
What finally is not for
Don't write logic that produces results in finally. The block runs regardless of outcome — it doesn't know whether the try succeeded. If you put commit() in finally, you'll commit even on failure.
And don't return from finally. This is one of the genuinely hazardous corners of the language:
try {
return 1;
} finally {
return 2; // wins — the method returns 2 and the original return is lost
}The return (or throw) inside finally overrides any return or exception from the try. The exception that was about to propagate is silently discarded. Most linters flag this as an error for that reason. The rule: finally does cleanup; finally does not produce values.
Order of execution
When both a catch and a finally are present:
try { ... }
catch { ... }
finally { ... }The order is exactly the order you'd guess: try runs, if it throws and a catch matches that catch runs, and finally runs after whichever of those did. If finally then throws, that new throw replaces whatever was propagating from the try or catch — another reason to keep finally quiet.
A worked example
We instrument a tiny "transaction" with try/catch/finally and call it three different ways: normal success, a recoverable failure, and an unrecoverable one. The finally runs in all three cases, which is the entire point.
In the third call, doWork throws a RuntimeException that the local catch doesn't match. The finally still runs and prints "release lock" before the exception keeps unwinding up to main. That's the property you want from cleanup code — it doesn't depend on whether handling succeeded.
What's next
The "open a resource, work with it, close it in finally" shape is so common Java built a dedicated statement for it. Continue to Java try-with-resources.
Practice
What does this method return?\n\n```java\nstatic int demo() {\n try {\n return 1;\n } finally {\n return 2;\n }\n}\n```