What happens if an exception is thrown inside a finally block, or if finally has a return statement?
Quick Answer
An exception thrown inside finally propagates and replaces (silently discards) any exception that was already propagating from the try/catch block. A return statement inside finally similarly overrides any return value or exception from the try/catch — the finally block's return always wins, which is why explicit returns inside finally are considered a serious anti-pattern.
Detailed Answer
finally always runs, but it can also override whatever the try/catch was about to do — which is a classic source of subtle bugs:
1. An exception thrown in finally masks the original exception:
static void risky() {
try {
throw new RuntimeException("original");
} finally {
throw new IllegalStateException("from finally"); // this one wins
}
}
// Caller only ever sees IllegalStateException — "original" is silently lost
(Note: this differs from try-with-resources, where a close-time exception is suppressed and attached rather than discarding the original — plain finally blocks give you no such safety net.)
2. A return in finally overrides any pending return value or exception:
static int test() {
try {
return 1;
} finally {
return 2; // this value is returned instead — 1 is discarded
}
}
// test() returns 2
static int test2() {
try {
throw new RuntimeException();
} finally {
return 42; // swallows the exception entirely — no exception propagates!
}
}
// test2() returns 42, and the RuntimeException never reaches the caller
Because of these surprises, returning (or throwing) from within a finally block is considered a serious anti-pattern and is flagged by most linters/static analyzers — finally should be reserved purely for cleanup (closing resources, releasing locks), never for controlling the method's outcome.