Question

Does a finally Block Always Execute in Java? Exceptions, Return, and Edge Cases

javaerror-handlingreturntry-catch-finally

Question

In Java, can you be completely certain that a finally block will always execute, regardless of what something() does?

For example:

try {
    something();
    return success;
} catch (Exception e) {
    return failure;
} finally {
    System.out.println("I don't know if this will get printed out");
}

I want to know whether the finally block is guaranteed to run in all cases, including when the try block returns a value or when an exception occurs.

Short Answer

By the end of this page, you will understand what Java's finally block is for, when it normally executes, how it behaves with return statements and exceptions, and the important edge cases where it may not run. You will also see how finally compares with modern alternatives like try-with-resources.

Concept

In Java, a finally block is used to run cleanup code after a try/catch block. Its main purpose is to ensure that important cleanup work happens whether the main code succeeds or fails.

Typical cleanup tasks include:

  • Closing files
  • Releasing database connections
  • Unlocking locks
  • Cleaning temporary state

Normal behavior

In normal Java execution, a finally block runs:

  • after the try block finishes
  • after a catch block finishes
  • even if the try or catch block contains return
  • even if an exception is thrown and caught

So for code like this:

try {
    return 1;
} finally {
    System.out.println("Runs before method actually returns");
}

the finally block runs before the method returns 1.

Why this matters

Without finally, cleanup code could be skipped whenever an error happens or when a method exits early. That can lead to resource leaks, locked files, open connections, or inconsistent state.

Important edge cases

Although finally is usually executed, it is not an absolute guarantee in every possible situation. It may not run if:

  • the JVM shuts down abruptly, such as with System.exit()
  • the process is forcibly killed
  • the machine loses power
  • the JVM crashes due to a fatal error
  • the thread never gets a chance to continue execution

So the practical rule is:

  • During normal program flow, yes, finally runs.
  • In abnormal JVM termination scenarios, no language feature can guarantee execution.

In your example

For this code:

try {
    something();
    return success;
} catch (Exception e) {
    return failure;
} finally {
    System.out.println("I don't know if this will get printed out");
}

System.out.println(...) will run in normal execution whether:

  • something() completes successfully
  • something() throws an Exception caught by the catch
  • the try block reaches return success
  • the catch block reaches return failure

But if something() terminates the JVM or the program is forcibly stopped, the finally block may not execute.

Mental Model

Think of try, catch, and finally like leaving a room:

  • try is the main task you came to do.
  • catch is what you do if something goes wrong.
  • finally is turning off the lights and locking the door before leaving.

Even if you finish your task early and head for the exit with return, Java still says, "Wait—do the cleanup first."

But if the whole building suddenly disappears—like a power failure or forced shutdown—there may be no chance to lock the door. That is why finally is reliable in normal execution, but not magical in catastrophic situations.

Take Quiz

Syntax and Examples

Basic syntax

try {
    // code that may fail
} catch (Exception e) {
    // handle the exception
} finally {
    // cleanup code
}

Example 1: finally runs after success

public static int demo() {
    try {
        System.out.println("In try");
        return 10;
    } finally {
        System.out.println("In finally");
    }
}

Output:

In try
In finally

The method still returns 10, but only after the finally block runs.

Example 2: finally runs after a caught exception

public static String demo() {
    try {
        throw new RuntimeException("Boom");
    } catch (RuntimeException e) {
        System.out.println("In catch");
        return "failed";
    } finally {
        System.out.println("In finally");
    }
}

Output:

In catch
In finally

Again, finally runs before the method returns.

Example 3: finally can override a return value

public static int demo() {
    try {
        return 1;
    } finally {
        return 2;
    }
}

This returns 2.

That is legal Java, but it is strongly discouraged because it hides what happened in the try block and can also suppress exceptions.

Example 4: finally may not run if the JVM exits

public static void demo() {
    try {
        System.out.println("In try");
        System.exit(0);
    } finally {
        System.out.println("This will likely not print");
    }
}

Here, the JVM exits before normal control flow can continue, so finally does not get a chance to run.

Step by Step Execution

Consider this method:

public static String check() {
    try {
        System.out.println("Step 1: try starts");
        return "success";
    } catch (Exception e) {
        System.out.println("Step 2: catch runs");
        return "failure";
    } finally {
        System.out.println("Step 3: finally runs");
    }
}

What happens step by step

  1. The try block starts.
  2. System.out.println("Step 1: try starts") runs.
  3. Java sees return "success";.
  4. Before the method actually returns, Java executes the finally block.
  5. System.out.println("Step 3: finally runs") runs.
  6. The method returns "success".

Output:

Step 1: try starts
Step 3: finally runs

Returned value:

success

If an exception happens instead

public static String check() {
    try {
        System.out.println("Step 1: try starts");
        throw new Exception();
    } catch (Exception e) {
        System.out.println("Step 2: catch runs");
        return "failure";
    } finally {
        System.out.println("Step 3: finally runs");
    }
}

Execution order:

  1. try starts.
  2. An exception is thrown.
  3. catch handles the exception.
  4. Java sees return "failure";.
  5. Before returning, Java runs finally.
  6. The method returns "failure".

Output:

Step 1: try starts
Step 2: catch runs
Step 3: finally runs

Real World Use Cases

finally is mainly used for cleanup that must happen after work is attempted.

Common examples

Closing a file or stream

InputStream in = null;
try {
    in = new FileInputStream("data.txt");
    // read file
} catch (IOException e) {
    // handle error
} finally {
    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            // handle close error
        }
    }
}

Releasing a lock

lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}

This is a very common and important use of finally.

Cleaning up temporary state

boolean previous = loggingEnabled;
loggingEnabled = false;
try {
    // do work with logging disabled
} finally {
    loggingEnabled = previous;
}

Database or network cleanup

Older Java code often uses finally to close:

  • JDBC connections
  • statements
  • result sets
  • sockets

In newer Java, try-with-resources is often preferred for these cases.

Take Quiz

Real Codebase Usage

In real projects, developers use finally for cleanup that must happen no matter how the earlier code exits.

Common patterns

Guarded resource cleanup

Older codebases often use null checks inside finally:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("input.txt"));
    return reader.readLine();
} finally {
    if (reader != null) {
        reader.close();
    }
}

Lock handling

This is still very common in modern code:

lock.lock();
try {
    processSharedData();
} finally {
    lock.unlock();
}

Timing or metrics collection

long start = System.currentTimeMillis();
try {
    runTask();
} finally {
    long duration = System.currentTimeMillis() - start;
    System.out.println("Task took " + duration + "ms");
}

Resetting state

setProcessing(true);
try {
    doWork();
} finally {
    setProcessing(false);
}

Modern preference: try-with-resources

For Closeable or AutoCloseable resources, Java developers usually prefer try-with-resources because it is clearer and safer:

try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
    return reader.readLine();
}

This reduces boilerplate and avoids many manual cleanup mistakes.

Common Mistakes

1. Assuming finally means literally always

Beginners often hear that finally "always runs" and take that as an absolute rule.

That is not fully correct.

Problem cases

  • System.exit()
  • forced process termination
  • JVM crash
  • machine shutdown

2. Returning from finally

This is one of the most dangerous mistakes.

Broken example

public static int test() {
    try {
        return 1;
    } finally {
        return 2;
    }
}

This returns 2, not 1.

It can also hide exceptions:

public static int test() {
    try {
        throw new RuntimeException("boom");
    } finally {
        return 2;
    }
}

The exception is suppressed, and the method returns 2.

Avoid it

Do not return from finally unless you have an extremely unusual reason.

3. Throwing new exceptions from finally carelessly

try {
    throw new RuntimeException("original");
} finally {
    throw new RuntimeException("new one");
}

The new exception replaces the original one.

That makes debugging harder.

4. Using finally for resource handling when try-with-resources is better

Manual cleanup is easy to get wrong:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("file.txt"));
} finally {
    if (reader != null) {
        reader.close();
    }
}

Prefer:

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    // use reader
}

5. Catching overly broad exceptions without good reason

catch (Exception e) {
    return failure;
}

This may hide useful information or swallow exceptions you did not intend to handle.

Catch the most specific exception type you can.

6. Putting essential business logic in finally

finally should usually be for cleanup, not core application decisions. Keep it focused on cleanup or guaranteed post-processing.

Comparisons

ConceptPurposeRuns on success?Runs on exception?Best use
tryMain code to attemptYesStarts hereCode that may fail
catchHandle exceptionsNoYes, if matchedError handling
finallyCleanup or guaranteed follow-upYesYes, usuallyReleasing resources, unlocking
try-with-resourcesAutomatic resource cleanupYesYesFiles, streams, DB resources

finally vs try-with-resources

Featurefinallytry-with-resources
Manual cleanupYesNo
Good for locks/state resetYesUsually not
Good for streams/filesPossiblePreferred
BoilerplateMoreLess
Safer for AutoCloseable resourcesLessMore

catch vs finally

  • Use catch when you want to handle an error.
  • Use finally when you want to run cleanup code regardless of outcome.

return inside try vs return inside finally

LocationEffect
return in trySchedules a return, then finally runs
return in catchSchedules a return, then finally runs
return in finallyOverrides earlier return values and may suppress exceptions
Take Quiz

Cheat Sheet

Quick rules

  • finally usually runs after try and catch.
  • finally still runs if try or catch contains return.
  • finally is mainly for cleanup.
  • finally does not guarantee execution if the JVM stops abruptly.
  • Avoid return inside finally.
  • Prefer try-with-resources for files, streams, and other AutoCloseable resources.

Core syntax

try {
    // risky code
} catch (Exception e) {
    // handle error
} finally {
    // cleanup
}

Common behavior

try {
    return 1;
} finally {
    System.out.println("runs first");
}
  • prints runs first
  • then returns 1

Dangerous pattern

try {
    return 1;
} finally {
    return 2;
}
  • returns 2
  • hides the earlier return

Cases where finally may not run

  • System.exit(...)
  • JVM crash
  • forced kill by OS or container
  • power failure

Best uses

  • lock.unlock()
  • resetting flags
  • metrics/timing cleanup
  • legacy resource cleanup

Prefer this for closeable resources

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    return reader.readLine();
}

FAQ

Does finally always run after a return in Java?

Yes, in normal execution. Java runs the finally block before the method actually returns.

Can a finally block fail to execute?

Yes. It may not run if the JVM exits abruptly, the process is killed, or the machine crashes.

Does finally run if an exception is thrown?

Yes, if execution continues normally through Java's exception handling. It runs whether the exception is caught or propagates upward, unless the JVM stops abruptly.

Can finally change the return value?

Yes. If you return from finally, it overrides earlier returns. This is usually a bad idea.

Is finally still useful in modern Java?

Yes, especially for unlocking, resetting state, or guaranteed follow-up actions. But for closing files and streams, try-with-resources is usually better.

What is better than finally for closing files in Java?

Try-with-resources is usually the best choice for files, streams, sockets, and other AutoCloseable objects.

Does finally run if the catch block returns?

Yes. A return in catch still causes finally to run before the method exits.

Take Quiz

Mini Project

Description

Build a small Java program that demonstrates how finally behaves in three common situations: normal success, caught exception, and JVM shutdown. This project helps you see that finally usually runs, but not in every possible case.

Goal

Create a program that prints and compares the behavior of finally during normal control flow, exception handling, and abrupt JVM exit.

Requirements

  • Write one method where the try block returns successfully and finally prints a message.
  • Write one method where the try block throws an exception, the catch block returns a value, and finally prints a message.
  • Write one method that calls System.exit(0) inside try.
  • Call the methods from main and observe which messages are printed.
  • Keep the code in plain Java without external libraries.
Take Quiz

Keep learning

Related questions

View more