HuntExams Academy logo
JavaBeginner#exceptions

What is the order of execution for try, catch, and finally?

Answer

Order: the try block runs; if an exception is thrown, the first matching catch runs; then finally runs. finally executes on every exit path \u2014 normal completion, a caught exception, an uncaught exception propagating out, and even a return, break or continue inside try or catch. Precise sequence: 1. Statements in try, up to the point of failure. 2. The matching catch. Only one catch runs, and the JVM picks the first compatible type \u2014 so subclasses must be listed before superclasses or the code will not compile. 3. finally, always. This is where you close resources, release locks and stop timers. 4. Code after the whole try statement, if control did not leave the method. The return-value rule that gets asked constantly: if try contains `return x`, the value of x is computed and saved FIRST, then finally runs, then the saved value is returned. Mutating that local variable inside finally does not change what was already returned. But an explicit return inside finally overrides the earlier return — and worse, it silently discards a pending exception. Never return or throw from finally. Other points: - finally does not run if the JVM exits via System.exit, the thread is killed, or a fatal error occurs. - try-with-resources closes resources BEFORE catch and finally run, in reverse order of creation, and attaches any close() failure as a suppressed exception. Prefer it over manual finally-close blocks. - If try and finally both throw, the exception from finally wins and the original is lost. - try { } finally { } with no catch is legal; catch without try is not.

Example

static int demo() {
    int x = 1;
    try {
        return x;          // the value 1 is captured HERE
    } finally {
        x = 99;            // runs, but the returned value stays 1
        System.out.println("finally");
    }
}
demo();    // prints "finally", returns 1

// Order with an exception
try {
    System.out.println("A");
    throw new IllegalStateException("boom");
} catch (IllegalStateException e) {
    System.out.println("B " + e.getMessage());
} finally {
    System.out.println("C");
}
System.out.println("D");
// A -> B boom -> C -> D

// Bad: finally swallows the exception
try { throw new RuntimeException("real cause"); }
finally { return; }          // exception silently lost -- never do this

// Preferred: try-with-resources closes before catch/finally
try (BufferedReader r = new BufferedReader(new FileReader("a.txt"))) {
    return r.readLine();
} catch (IOException e) {
    return null;
}

Detailed explanation

In Java, execution starts with the try block. If it completes normally, all catch blocks are skipped and finally executes. If a statement throws an exception, the remaining statements in try are skipped, and Java selects the first catch whose parameter type can handle that exception. Only one catch executes; finally then runs. For example, if try prints A and throws an IllegalStateException, its matching catch prints B, and finally prints C, the output order is A, B, C.

Catch blocks are checked in their written order, so a more specific exception type must appear before a superclass type that would also catch it. Otherwise, the later catch is unreachable and compilation fails. If no catch matches, finally executes before the exception propagates outward. An exception thrown inside a catch is not handled by another catch attached to the same try. It also passes through finally before propagating. Code after the complete statement runs only when control reaches it normally, not when a return or exception carries control elsewhere.

Finally also executes before a pending return, break, or continue completes, provided control leaves the associated try or catch. With return x, Java evaluates and saves the return value before executing finally. For example, if x is 1 when return x executes, assigning x = 99 inside finally still leaves the returned integer as 1. For an object reference, however, finally can mutate the referenced object's contents. A return or exception from finally replaces the pending return or exception, potentially hiding the original failure; avoid such control flow.

Finally is intended for cleanup, but it is not an unconditional guarantee: JVM termination through System.exit, a process crash, or code that never exits try can prevent execution. Ordinary exception propagation, including many Errors, still executes finally. For AutoCloseable resources, prefer try-with-resources: successfully initialized resources close in reverse initialization order before associated catch and finally blocks run. If the body throws and closing also fails, the closing failure is suppressed on the primary exception. A try-finally statement without catch is also legal.

Key points to remember

  • Normal execution: try → finally → following code.
  • Handled exception: try until failure → first matching catch → finally.
  • Unhandled exception: try until failure → finally → propagation to an outer handler.
  • A pending return value is evaluated before finally; finally can override pending control flow.
  • Use try-with-resources for automatic resource closing, and put specific catch types before general ones.

Common mistakes candidates make

  • Saying finally always runs without acknowledging JVM termination or a try block that never exits.
  • Assuming every catch executes, or that sibling catch blocks handle exceptions thrown by one another.
  • Returning from finally or allowing cleanup exceptions to mask the original exception.

Likely follow-up questions

Does changing a variable in finally change the returned value?

Not if the return expression has already evaluated to a primitive value: return x saves that value before finally runs. If the saved value is an object reference, mutating that object's fields in finally can affect what the caller observes, but reassigning the local reference does not.

What happens if both try and finally throw exceptions?

The exception thrown by finally propagates, replacing the original exception. Unlike try-with-resources, ordinary finally does not automatically preserve the original failure as a suppressed exception.

What if close() fails in try-with-resources?

If the body already threw an exception, that remains the primary exception and closing failures are attached as suppressed exceptions, accessible through getSuppressed(). If there is no pending exception, the first closing failure becomes the primary exception, and subsequent closing failures are suppressed.

Interview tip: Explain the normal and exceptional sequences first, then demonstrate the saved return-value rule with a small integer example.

Related Interview Questions

1
JavaAdvanced#exceptions

What happens if an exception is thrown in a finally block?

Open
2
JavaIntermediate#exceptions

What is multi-catch in Java 7+?

Open
3
JavaIntermediate#concurrency

What is a race condition and how do you prevent it?

Open