00:00

finally Block in Java – Explained with Examples

The finally block contains code that runs after a try block no matter how the try block ends: normally, with a caught exception, with an uncaught exception, or with return, break or continue. That makes it the classic place for clean-up work such as closing files, releasing locks and resetting state.

This tutorial shows every situation in which finally runs, how it interacts with return, the rare cases where it does not run, and why try-with-resources is often a better choice for closing resources.

1. Syntax

try-catch-finally and try-finally
try {
    // code that may throw
} catch (SomeException e) {
    // optional handler
} finally {
    // always runs
}

try {
    // code
} finally {
    // clean-up without catching anything
}
Flow of try, catch and finally in Java

2. When the finally Block Runs

FinallyCases.java
public class FinallyCases {
    static void run(String label, int divisor) {
        System.out.println(label);
        try {
            System.out.println("  try: result = " + (10 / divisor));
        } catch (ArithmeticException e) {
            System.out.println("  catch: " + e.getMessage());
        } finally {
            System.out.println("  finally: always runs");
        }
    }

    public static void main(String[] args) {
        run("Case 1: no exception", 2);
        run("Case 2: exception caught", 0);

        System.out.println("Case 3: exception NOT caught");
        try {
            try {
                Object s = "text";
                Integer n = (Integer) s;               // ClassCastException, no matching catch
            } finally {
                System.out.println("  finally: runs before the exception leaves");
            }
        } catch (ClassCastException e) {
            System.out.println("  handled further up: " + e.getClass().getSimpleName());
        }
    }
}
Output
Case 1: no exception
  try: result = 5
  finally: always runs
Case 2: exception caught
  catch: / by zero
  finally: always runs
Case 3: exception NOT caught
  finally: runs before the exception leaves
  handled further up: ClassCastException
SituationOrder of execution
No exceptiontry → finally → code after
Exception caughttry (stops) → catch → finally → code after
Exception not caughttry (stops) → finally → exception leaves the method
return, break or continue in tryfinally runs before control actually leaves

3. finally with return, break and continue

FinallyAndReturn.java
public class FinallyAndReturn {
    static String withReturn() {
        try {
            return "value from try";
        } finally {
            System.out.println("finally runs before the method returns");
        }
    }

    @SuppressWarnings("finally")
    static String overridingReturn() {
        try {
            throw new IllegalStateException("important error");
        } finally {
            return "finally wins";                 // BAD: hides the exception
        }
    }

    public static void main(String[] args) {
        System.out.println(withReturn());
        System.out.println(overridingReturn() + " (the exception was silently lost)");

        for (int i = 1; i <= 3; i++) {
            try {
                if (i == 2) continue;
                if (i == 3) break;
                System.out.println("loop body " + i);
            } finally {
                System.out.println("finally for i=" + i);  // runs on continue and break too
            }
        }
    }
}
Output
finally runs before the method returns
value from try
finally wins (the exception was silently lost)
loop body 1
finally for i=1
finally for i=2
finally for i=3
  • When try returns a value, the value is computed first, then finally runs, then the method returns.
  • finally also runs when a loop iteration ends with continue or break.
  • Never return or throw from a finally block. As overridingReturn() shows, a return in finally replaces the try block's result and silently discards any exception, hiding real errors.

4. Using finally for Clean-up

Before try-with-resources existed, finally was the standard way to make sure resources were released even when an operation failed:

CleanupWithFinally.java
public class CleanupWithFinally {
    static class Connection {
        private final String name;
        Connection(String name) { this.name = name; System.out.println("open " + name); }
        void query(boolean fail) {
            if (fail) throw new IllegalStateException("query failed");
            System.out.println("query ok on " + name);
        }
        void close() { System.out.println("close " + name); }
    }

    static void report(boolean fail) {
        Connection c = new Connection("db");
        try {
            c.query(fail);
        } finally {
            c.close();                              // runs whether query succeeded or failed
        }
    }

    public static void main(String[] args) {
        report(false);
        try {
            report(true);
        } catch (IllegalStateException e) {
            System.out.println("caller saw: " + e.getMessage());
        }
    }
}
Output
open db
query ok on db
close db
open db
close db
caller saw: query failed

The connection was closed in both cases, and the failure still reached the caller, because a try-finally does not catch the exception; it only adds clean-up.

Modern alternative: for anything that implements AutoCloseable (files, streams, sockets, JDBC connections), use try-with-resources. It closes resources automatically, in the right order, and handles exceptions thrown while closing. Use finally for other clean-up such as releasing a Lock or restoring a flag.

5. When finally Does Not Run

Only a few situations stop a finally block from running:

FinallyNotRun.java
public class FinallyNotRun {
    public static void main(String[] args) {
        try {
            System.out.println("calling System.exit(0)");
            System.exit(0);                         // the JVM stops immediately
        } finally {
            System.out.println("this finally block never runs");
        }
    }
}
Output
calling System.exit(0)
  • System.exit() or Runtime.halt() is called in the try or catch block.
  • The JVM crashes or the process is killed (for example with kill -9).
  • The thread running the try block never finishes, for example an infinite loop or a deadlock.
  • A daemon thread is still inside the try block when the JVM shuts down.

6. Rules

  • A try statement can have at most one finally block, and it comes after all catch blocks.
  • try needs at least a catch or a finally (or resources).
  • Variables declared in the try block are not visible in finally; declare them before the try.
  • If finally throws an exception, it replaces any exception from the try or catch block, so guard risky clean-up with its own try-catch.

7. Summary

  • finally runs after try (and catch), whether an exception occurred or not.
  • It runs even after return, break and continue.
  • Use it for clean-up; prefer try-with-resources for closeable resources.
  • Do not return or throw from finally; it hides results and exceptions.
  • It does not run after System.exit(), a JVM crash, or if the try block never ends.

Related Tutorials