Back to try-catch-finally
Q1Question 1 of 4

How does the finally block work?

Key Concepts
  • finally runs after try and catch regardless of whether an exception occurred, making it the guaranteed cleanup location
  • finally executes even when try or catch contains a return statement, running before the return value is sent to the caller
  • finally does NOT run when System.exit() is called, the JVM crashes, Runtime.halt() is invoked, or an infinite loop blocks try
  • Never return or throw from finally because it silently overrides the try block's return value or swallows its exception
  • Modern Java uses try-with-resources instead of finally for AutoCloseable resource cleanup
  • The execution order is: try body, then catch (if exception), then finally, then return to caller

Definition

The finally block always executes after try and catch for guaranteed cleanup, except when System.exit() is called, the JVM crashes, Runtime.halt() is invoked, or an infinite loop prevents reaching it.
  • finally is a block that executes after the try block (and catch, if present) regardless of whether an exception occurred.
  • It is guaranteed to run when: try succeeds, try throws and catch handles it, try throws and catch re-throws, or try/catch contains a return statement.
  • It does not run in four situations: System.exit() shuts down the JVM before finally, a JVM crash (SIGKILL, native error) kills the process, Runtime.halt() stops the JVM immediately, or an infinite loop or deadlock in try prevents reaching finally.
  • The modern replacement is try-with-resources for AutoCloseable resources, which is cleaner and handles the dual-exception scenario correctly.

The finally block is the end-of-day vault-locking procedure at National Bank of India. No matter what happened during the day (successful transactions, failed transactions, customer complaints), the vault must be locked at closing time. Even if there was a fire alarm (exception), the vault gets locked. But if the entire building collapses (JVM crash) or someone cuts the main power (System.exit), there is no time to lock anything.

Understanding the Concepts

What is a finally block?

The finally block is a section of code that executes after the try block (and any catch block) completes, regardless of whether an exception was thrown. It provides a guaranteed cleanup point for releasing resources like database connections, file handles, and locks.

JAVA
package com.bank.demo;

import java.math.BigDecimal;

public class FinallyBasicDemo {

    public static void main(String[] args) {
        System.out.println("=== No exception ===");
        try {
            BigDecimal balance = new BigDecimal("75000.00");
            System.out.println("Balance: Rs." + balance);
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            System.out.println("Finally: audit log written");
        }

        System.out.println("\n=== With exception ===");
        try {
            int result = 100 / 0;
            System.out.println("Never reached: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Caught: " + e.getMessage());
        } finally {
            System.out.println("Finally: cleanup runs after exception too");
        }
    }
}
TEXT
=== No exception ===
Balance: Rs.75000.00
Finally: audit log written

=== With exception ===
Caught: / by zero
Finally: cleanup runs after exception too

Does finally run when try contains a return statement?

Yes. The finally block executes even when the try or catch block contains a return statement. The finally block runs before the return value is actually delivered to the caller.

JAVA
package com.bank.demo;

import java.math.BigDecimal;

public class FinallyWithReturnDemo {

    public static BigDecimal getBalance(String accountId) {
        try {
            System.out.println("Try: fetching balance for " + accountId);
            return new BigDecimal("50000.00");
        } catch (Exception e) {
            System.out.println("Catch: error occurred");
            return BigDecimal.ZERO;
        } finally {
            System.out.println("Finally: runs BEFORE return value is sent to caller");
        }
    }

    public static void main(String[] args) {
        BigDecimal balance = getBalance("SAV2001");
        System.out.println("Returned balance: Rs." + balance);
    }
}
TEXT
Try: fetching balance for SAV2001
Finally: runs BEFORE return value is sent to caller
Returned balance: Rs.50000.00

When does finally NOT execute?

There are four situations where the finally block does not run.

JAVA
package com.bank.demo;

public class FinallyNotExecuteDemo {

    // 1. System.exit() shuts down the JVM
    public static void systemExitExample() {
        try {
            System.out.println("Before System.exit()");
            // System.exit(0);  // Uncomment to see: finally does NOT run
            System.out.println("(System.exit commented out for demo)");
        } finally {
            System.out.println("Finally: would NOT run after System.exit()");
        }
    }

    // 2. Runtime.halt() is even more abrupt than System.exit()
    // Runtime.getRuntime().halt(1) skips shutdown hooks AND finally

    // 3. JVM crash (SIGKILL, native error) kills the process
    // Nothing runs. The process is dead.

    // 4. Infinite loop in try prevents reaching finally
    public static void infiniteLoopExample() {
        try {
            // while (true) { }  // Infinite loop, never reaches finally
            System.out.println("(Infinite loop commented out for demo)");
        } finally {
            System.out.println("Finally: unreachable if loop is infinite");
        }
    }

    public static void main(String[] args) {
        systemExitExample();
        System.out.println();
        infiniteLoopExample();
    }
}
TEXT
Before System.exit()
(System.exit commented out for demo)
Finally: would NOT run after System.exit()

(Infinite loop commented out for demo)
Finally: unreachable if loop is infinite

Why is returning from finally dangerous?

A return statement in finally overrides the return value from try or catch. Worse, if try threw an exception, a return in finally swallows the exception entirely.

JAVA
package com.bank.demo;

public class FinallyReturnDangerDemo {

    // WRONG: finally return overrides try return
    public static int wrongReturn() {
        try {
            return 1;  // try says return 1
        } finally {
            return 2;  // finally overrides: caller gets 2
        }
    }

    // WRONG: finally return swallows exception
    public static String wrongSwallow() {
        try {
            throw new RuntimeException("CRITICAL ERROR");
        } finally {
            return "everything is fine";  // exception is GONE
        }
    }

    // CORRECT: never return from finally
    public static int correctApproach() {
        int result = 0;
        try {
            result = 1;
            return result;
        } finally {
            // Cleanup only, no return
            System.out.println("Cleanup complete, returning " + result);
        }
    }

    public static void main(String[] args) {
        System.out.println("Wrong return: " + wrongReturn());
        System.out.println("Wrong swallow: " + wrongSwallow());
        System.out.println("Correct: " + correctApproach());
    }
}
TEXT
Wrong return: 2
Wrong swallow: everything is fine
Cleanup complete, returning 1
Correct: 1

How is try-with-resources better than finally?

try-with-resources (Java 7+) automatically closes any resource that implements AutoCloseable. It eliminates the verbose null-check pattern and correctly handles the dual-exception scenario using suppressed exceptions.

JAVA
package com.bank.demo;

import java.io.BufferedReader;
import java.io.StringReader;

public class FinallyVsTryWithResources {

    // OLD: verbose finally pattern
    public static void oldPattern() {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new StringReader("SAV2001,50000"));
            System.out.println("Old: " + reader.readLine());
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    System.out.println("Close failed: " + e.getMessage());
                }
            }
        }
    }

    // NEW: clean try-with-resources
    public static void newPattern() {
        try (BufferedReader reader = new BufferedReader(new StringReader("CUR3001,75000"))) {
            System.out.println("New: " + reader.readLine());
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
        // reader.close() called automatically. No finally needed.
    }

    public static void main(String[] args) {
        oldPattern();
        newPattern();
    }
}
TEXT
Old: SAV2001,50000
New: CUR3001,75000

What is the execution order of try-catch-finally?

The execution order follows a predictable pattern: try body first, then catch if an exception occurred, then finally regardless, then the return value is delivered to the caller.

JAVA
package com.bank.demo;

public class ExecutionOrderDemo {

    public static String traceOrder(boolean shouldThrow) {
        StringBuilder trace = new StringBuilder();
        try {
            trace.append("try ");
            if (shouldThrow) throw new RuntimeException("error");
            trace.append("success ");
            return trace.append("return-try").toString();
        } catch (RuntimeException e) {
            trace.append("catch ");
            return trace.append("return-catch").toString();
        } finally {
            trace.append(" finally");
            // Do NOT return here. The return from try/catch is preserved.
        }
    }

    public static void main(String[] args) {
        System.out.println("No exception: " + traceOrder(false));
        System.out.println("With exception: " + traceOrder(true));
    }
}
TEXT
No exception: try success return-try finally
With exception: try catch return-catch finally

What should a senior engineer know?

  • try-finally without catch is valid. try { ... } finally { ... } compiles. Use when you need cleanup but want the exception to propagate uncaught.
  • System.exit in production. Never call System.exit() in application code. Use Spring's graceful shutdown or container lifecycle management instead.
  • Daemon threads and finally. If all non-daemon threads finish, the JVM exits without running finally blocks in daemon threads.
  • Thread interruption. If a thread is interrupted during a sleep/wait in the try block, the InterruptedException is thrown and finally still runs normally.

What are common interview follow-up questions?

  • Can you have try-finally without catch? (Yes, it is valid.)
  • Does finally run if catch re-throws? (Yes.)
  • Does finally run if there is a return in try? (Yes, before the return completes.)
  • What if both try and finally return? (finally's return wins.)
  • What if both try and finally throw? (finally's throw wins and try's exception is lost.)

Visual Overview

DIAGRAM
  ┌──────────────────────┐
  │      try block        │
  │  risky code here      │
  └───────┬───────┬───────┘
          │       │
     success    exception
          │       │
          │  ┌────▼────────────┐
          │  │   catch block    │
          │  │  handle error    │
          │  └────┬────────────┘
          │       │
          └───┬───┘
              │
       ┌──────▼──────────────┐
       │   finally block      │
       │  ALWAYS runs         │
       │  cleanup resources   │
       └──────┬──────────────┘
              │
              ▼
       return to caller

  Does NOT run:
  ┌─────────────────────────┐
  │ System.exit()           │
  │ JVM crash (kill -9)     │
  │ Runtime.halt()          │
  │ Infinite loop in try    │
  └─────────────────────────┘

Key Takeaway

The finally block is Java's guarantee that cleanup code will run after try and catch, regardless of whether an exception occurred. It executes when try succeeds, when try throws and catch handles it, when catch re-throws, and even when try or catch contains a return statement. The execution order is: try, then catch (if exception), then finally, then the return value is delivered.

Finally does not run in four situations: System.exit() terminates the JVM, a JVM crash (SIGKILL, native error) kills the process, Runtime.halt() stops everything immediately, and an infinite loop or deadlock in the try block prevents reaching finally. In production at National Bank of India, Pankaj never uses System.exit() in application code because it bypasses finally blocks and can leave database connections open.

The most important rule is: never return or throw from a finally block. A return in finally overrides the try/catch return value, and a throw in finally swallows the try's exception entirely. The modern replacement for finally is try-with-resources, which auto-closes resources and correctly handles dual exceptions using suppressed exceptions.

Remember one sentence: finally always runs after try-catch for guaranteed cleanup, except when the JVM itself is shut down or dead.

---

Share
Use ← → arrow keys to navigate