Back to Blog
Java

Java Finally Block: Execution and Pitfalls

Learn how the java finally block guarantees cleanup execution, interacts with return and exceptions, and when try-with-resources is a better choice.

exception handlingtry-with-resourcesresource cleanupjava syntax
Diagram showing try-catch-finally flow with a finally block always executing after try or catch.

The java finally block is part of the exception handling mechanism that executes after the try block completes, whether it finishes normally or throws an exception. It is intended for cleanup operations that must run regardless of the outcome, such as closing files, releasing locks, or restoring state.

How the finally Block Executes

The finally block is always executed when the try block exits, with one exception: if the JVM terminates abruptly (for example, via System.exit() or a crash), the finally block may not run. In normal flow, the sequence is:

  • If the try block completes normally, the finally block runs immediately after.
  • If the try block throws an exception, the matching catch block (if any) runs first, then the finally block.
  • If no catch block matches, the finally block still runs before the exception propagates up the call stack.

This guarantee makes finally suitable for releasing resources that are not automatically managed by the JVM.

Syntax and Placement of finally

The finally block is declared after a try block, optionally after one or more catch blocks. It cannot appear without a try block, and a try block does not require a catch if a finally is present.

try { // code that may throw } catch (SomeException e) { // handle exception } finally { // always runs }

A try-finally without a catch is legal and useful when the exception should propagate but cleanup still needs to happen.

Interaction with return Statements

A common misunderstanding is that a return statement in the try block prevents the finally block from executing. In fact, the finally block runs before the method actually returns. The return value is computed first, then the finally block executes, and then the method returns.

public static String getValue() { try { return "from try"; } finally { System.out.println("finally ran"); } }

Calling getValue() prints "finally ran" and returns "from try". However, if the finally block itself contains a return, it overrides the original return value. This is generally discouraged because it makes control flow difficult to follow.

Interaction with Exceptions and System.exit()

If an exception is thrown in the try block and the finally block also throws an exception, the exception from the finally block supersedes the original one. This can mask the root cause of a failure, so it is important to avoid throwing exceptions from finally unless absolutely necessary.

The most significant limitation is that System.exit() terminates the JVM immediately, so the finally block is not executed. The same applies to a fatal JVM error. Code that relies on finally for critical cleanup should be aware of this behavior.

finally and Resource Management

Before Java 7, finally was the standard way to close resources such as streams and connections. The pattern required explicit null checks and nested try-finally blocks, which was verbose and error-prone.

Java 7 introduced the try-with-resources statement, which automatically closes resources that implement AutoCloseable. It is more concise and eliminates the risk of forgetting to close a resource.

try (FileInputStream in = new FileInputStream("data.txt")) { // read from file } catch (IOException e) { // handle }

The resource is closed automatically, and if both the try block and the close operation throw exceptions, the original exception is preserved and the close exception is added as suppressed.

Comparing finally and try-with-resources

Aspectfinallytry-with-resources
Resource closingManual, must call close() explicitlyAutomatic for AutoCloseable resources
Exception suppressionClose exception overrides originalOriginal exception preserved, close exception suppressed
Code verbosityMore verbose, nested blocks commonConcise, single block
Best fitNon-AutoCloseable cleanup, locksI/O resources, JDBC connections

Use finally when the cleanup does not involve an AutoCloseable resource, such as releasing a lock or resetting a flag. For resources that implement AutoCloseable, prefer try-with-resources.

Common Pitfalls and Misconceptions

One frequent mistake is assuming that finally always runs. As noted, System.exit() and JVM crashes break that guarantee. Another is modifying the return value in finally, which can lead to surprising behavior. Additionally, throwing an exception from finally can hide the original exception, making debugging harder.

A more subtle issue is that finally can increase the scope of variables if not carefully structured. Variables declared inside try are not visible in finally, so any resource that needs cleanup must be declared outside.

Runtime Behavior and Performance

The finally block adds minimal runtime overhead. The JVM compiles it into bytecode that ensures the cleanup code runs on all normal and exceptional exits. In practice, the cost is negligible compared to the operations typically performed in cleanup, such as I/O or lock release.

The main performance concern is not the finally block itself, but the cost of the cleanup operations. For example, closing a database connection in a finally block is expensive, so it should not be done inside a tight loop. Instead, batch operations or connection pooling should be considered.

When Not to Use finally

If the only purpose of the finally block is to close an AutoCloseable resource, try-with-resources is a better choice. Finally is also not appropriate for operations that must survive a JVM shutdown, such as flushing critical data to disk; in that case, shutdown hooks are a more reliable mechanism.

Finally should also be avoided for control flow, such as conditionally returning different values. That logic belongs in the try or catch block, not in finally.

java finally block: Practical Usage and Code Examples | RYUSLOG DEV