error handling
finally block
try-catch-finally
Java programming
exception management

Why do we use finally blocks?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A finally block exists so that cleanup code can run whether an operation succeeds, fails, or returns early. It is most useful when some resource or side effect must be handled consistently, such as closing a stream, releasing a lock, or restoring temporary state.

What finally Guarantees

In a try-catch-finally structure, the finally block runs after the try and after any matching catch, even if:

  • an exception was thrown
  • an exception was caught
  • the method returns from inside try or catch

That makes finally the traditional place for cleanup code that must happen no matter what.

java
1public static void demo() {
2    try {
3        System.out.println("inside try");
4        return;
5    } finally {
6        System.out.println("finally still runs");
7    }
8}

Calling demo() prints both lines because the return does not skip finally.

The Classic Use Case: Cleanup

Imagine opening a resource manually. You want to make sure it is closed whether the work succeeds or an exception interrupts it.

java
1import java.io.BufferedReader;
2import java.io.FileReader;
3import java.io.IOException;
4
5public class Example {
6    public static void readFile(String path) throws IOException {
7        BufferedReader reader = null;
8        try {
9            reader = new BufferedReader(new FileReader(path));
10            System.out.println(reader.readLine());
11        } finally {
12            if (reader != null) {
13                reader.close();
14            }
15        }
16    }
17}

The key benefit is predictability. You do not have to remember to close the file on every code path separately.

Why finally Is Better Than Duplicating Cleanup

Without finally, people often write the same cleanup logic in multiple branches:

  • one close call in the success path
  • another in an error path
  • maybe a third after some conditional return

That repetition is fragile. One branch eventually forgets the cleanup, and the bug only appears under specific error conditions.

finally centralizes the cleanup rule in one place.

Modern Java Often Uses Try-With-Resources Instead

In modern Java, finally is still important to understand, but many resource-management cases are better expressed with try-with-resources.

java
1import java.io.BufferedReader;
2import java.io.FileReader;
3import java.io.IOException;
4
5public class Example {
6    public static void readFile(String path) throws IOException {
7        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
8            System.out.println(reader.readLine());
9        }
10    }
11}

This is usually cleaner than a manual finally for closeable resources. The language guarantees the resource is closed automatically.

So the practical rule is:

  • use try-with-resources for AutoCloseable resources
  • use finally for other cleanup that still must happen unconditionally

Other Good Uses for finally

finally is not limited to file handles. It is also useful for:

  • unlocking a lock
  • resetting thread-local or global state
  • stopping a timer or tracer span
  • restoring a UI or transaction flag

A locking example:

java
1import java.util.concurrent.locks.ReentrantLock;
2
3public class Counter {
4    private final ReentrantLock lock = new ReentrantLock();
5    private int value = 0;
6
7    public void increment() {
8        lock.lock();
9        try {
10            value++;
11        } finally {
12            lock.unlock();
13        }
14    }
15}

The unlock() call belongs in finally because forgetting it on an exception path can cause deadlocks.

What finally Does Not Guarantee

Developers sometimes overstate what finally can do. It is very reliable inside normal program execution, but it does not protect against every possible termination mode.

Examples where it may not run as you expect include:

  • the process is forcibly killed
  • the JVM crashes
  • 'System.exit() terminates the process before normal flow continues'

So finally is a control-flow guarantee inside the program, not a universal protection against all external failure modes.

Common Pitfalls

One common mistake is writing cleanup in both catch and finally. That can duplicate work or introduce inconsistent behavior.

Another is throwing a new exception from finally without care. That can hide the original exception and make debugging much harder.

People also sometimes keep using manual finally blocks for closeable resources even when try-with-resources would be simpler and safer.

Finally, finally should be for cleanup, not for ordinary business logic. If the block starts doing core application work, the control flow becomes harder to reason about.

Summary

  • 'finally exists so cleanup code runs whether the protected work succeeds or fails.'
  • It is traditionally used for releasing resources, unlocking locks, and restoring state.
  • In Java, finally still runs even if try returns early.
  • Try-with-resources is often the better modern choice for closeable resources.
  • 'finally is reliable for program control flow, but not for every kind of external process termination.'

Course illustration
Course illustration

All Rights Reserved.