Back to Blog
Java

Java Finally Block: Execution and Pitfalls

Learn when Java's finally block runs, how it interacts with return statements and exceptions, and why try-with-resources is often a better choice for closing resources.

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. It executes after the try block completes, whether that block 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 runs when the try block exits, with a notable exception: if the JVM terminates abruptly, for example via System.exit() or a crash, the finally block may not run. In normal program 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 behavior 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 when a fatal error prevents the JVM from continuing normal program execution. 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 handlingA close exception in a finally block can mask an earlier exception unless handled explicitlyOriginal 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. These are not mutually exclusive: a try-with-resources statement can also have catch and finally clauses, so you can combine automatic resource closing with other cleanup.

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 returning from finally, which can override a value returned from try or catch. Additionally, throwing an exception from finally can hide the original exception, making debugging harder.

A more subtle issue is that variables declared inside try are not visible in finally. A resource that must be cleaned up in finally needs to be declared in the surrounding scope, which may widen the variable's scope.

Runtime Behavior and Performance

The Java compiler arranges for the finally block's code to run on both normal and exceptional exits. The overhead of the finally block itself is usually small compared with the cleanup operations it contains, 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 reliable for cleanup that must run during JVM shutdown, such as flushing critical data to disk. Shutdown hooks run during an orderly shutdown, but they do not run on a hard crash or kill -9, so neither a finally block nor a shutdown hook can guarantee that the operation completes.

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: Execution, Pitfalls, and Resource Cleanup | RYUSLOG DEV