Is the execution of a finally block guaranteed?
The finally block always executes (with rare exceptions - see Middle Level). For beginners, consider finally as guaranteed to execute.
Junior Level
Basic rule
The finally block always executes (with rare exceptions - see Middle Level). For beginners, consider finally as guaranteed to execute.
try {
System.out.println("try");
} finally {
System.out.println("finally"); // Will definitely execute
}
// Output: try \n finally
With an exception
try {
throw new RuntimeException("Error");
} finally {
System.out.println("Cleanup"); // Still executes
}
// Output: Cleanup, then exception propagates further
Why finally is needed
For releasing resources - closing files, connections, locks:
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// work with file
} finally {
if (fis != null) fis.close(); // Will close in any case
}
In modern Java, use try-with-resources - it does this automatically.
Middle Level
When NOT to use finally
For closing resources, use try-with-resources instead of finally with close(). Use finally for: releasing locks (ReentrantLock.unlock()), restoring state (Thread.currentThread().interrupt()), logging.
How it works in bytecode
In old Java (before 6), the jsr (Jump to Subroutine) instruction was used. In modern Java, the compiler copies the finally block bytecode to all method exit branches - after try, after each catch, and in the implicit exception handler.
This increases method size but makes control flow linear.
When finally will NOT execute
System.exit(n)- forceful JVM terminationRuntime.halt(n)- even harsher, doesn’t even run Shutdown Hooks- Infinite loop in
try- thread never reaches exit - Deadlock - thread is blocked forever
- Kill -9 -
SIGKILLsignal kills the process at OS level - Daemon Threads - if only daemons remain, JVM terminates without waiting
- JVM crash -
VirtualMachineErrorat the moment of transition tofinally
Danger: overwriting return
int method() {
try { return 1; }
finally { return 2; }
}
// Returns 2! finally overwrites the return value
return in finally executes AFTER the value was already prepared in try. Finally has the last chance to change the result. In bytecode: the value from try is saved to a temporary variable, finally executes, and if it has its own return - it overwrites the result.
Danger: swallowing exceptions
try {
throw new RuntimeException("Original error");
} finally {
throw new RuntimeException("Finally error");
}
// Original exception is lost forever!
Senior Level
Shadowing - losing Root Cause
Shadowing - when one exception “hides” another. Root Cause - the very first exception in the chain that caused all others.
If exception A occurred in try, and exception B in finally, then A will be lost:
try {
// SQLException - DB unavailable
connection.executeQuery("...");
} finally {
// NPE - overshadows SQLException
connection.close(); // connection == null
}
// Logs only show NPE, no info about DB
Solution: try-with-resources adds exceptions from close() to the suppressed list, rather than overwriting the main one.
Correct pattern for locks
Lock lock = new ReentrantLock();
lock.lock(); // Lock BEFORE try
try {
// critical section
} finally {
lock.unlock(); // Always release
}
Make sure lock() was before try. If lock() throws, unlock() will try to release an unacquired lock -> IllegalMonitorStateException.
Locking Pitfall in finally
If finally calls a method that can block (writing to log on a full disk), you’ll hang the entire resource cleanup thread.
finally and try-with-resources
try (Resource r = new Resource()) {
// work
} catch (Exception e) {
// resources are ALREADY closed here
} finally {
// resources are also already closed
}
Manually written catch and finally blocks execute after automatic resource closing.
Diagnostics
- Deadlock in finally - if
finallycalls a blocking method, thread will hang - Keep code in
finallyas simple and fast as possible - Always protect
finallyfrom new exceptions - For analysis use
javap -c- you’ll see howfinallyis copied to all branches
Interview Cheat Sheet
Must know:
finallyalways executes (with rare exceptions)- When finally will NOT execute:
System.exit(),Runtime.halt(), infinite loop, deadlock, kill -9, daemon threads, JVM crash returninfinallyoverwrites value fromtry- returns value from finally- Exception in
finallyoverwrites the main exception fromtry(shadowing) - Compiler copies
finallybytecode to all branches - increases method size - For closing resources use try-with-resources, not finally
- Use finally for: releasing locks, restoring state, logging
catchandfinallyblocks execute AFTER automatic resource closing in TWR
Frequent follow-up questions:
- Can finally not execute? - Yes: System.exit(), kill -9, infinite loop, JVM crash
- What happens with return in finally? - Overwrites return value from try - this is a bug
- What is exception shadowing? - Exception from finally overwrites the main one from try; solution - try-with-resources with suppressed
- Correct pattern for locks? -
lock.lock()BEFORE try,lock.unlock()in finally
Red flags (NOT to say):
- “Finally executes absolutely always” - no, System.exit() and kill -9 stop JVM
- “I use return in finally to return a default value” - this is an anti-pattern, overwrites try result
- “I catch exception in finally and continue” - original exception is lost forever
Related topics:
- [[What is try-with-resources]]
- [[What are suppressed exceptions]]
- [[What are the requirements for resources in try-with-resources]]
- [[What is exception chaining]]
- [[How to properly log exceptions]]