Finally Block

Introduction

finally block always executes, whether exception occurs or not. Used for cleanup operations.


Basic Syntax

try {
    // Code that may throw exception
} catch (Exception e) {
    // Handle exception
} finally {
    // Always executes (cleanup)
}

Simple Example

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Try block");
            int result = 10 / 0;  // Exception
        } catch (ArithmeticException e) {
            System.out.println("Catch block");
        } finally {
            System.out.println("Finally block");  // Always runs
        }
    }
}

Output:

Try block
Catch block
Finally block

When finally Executes

Case 1: No Exception

try {
    System.out.println("No error");
} finally {
    System.out.println("Finally runs");  // Runs
}

Case 2: Exception Caught

try {
    int x = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Caught");
} finally {
    System.out.println("Finally runs");  // Runs
}

Case 3: Exception Not Caught

try {
    int x = 10 / 0;
} finally {
    System.out.println("Finally runs");  // Still runs!
}
// Then program terminates

Typical Use: Close Resources

import java.io.*;

public class Main {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("test.txt");
            // Read file
            System.out.println("Reading file");
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            // Close file (cleanup)
            try {
                if (fr != null) {
                    fr.close();
                    System.out.println("File closed");
                }
            } catch (IOException e) {
                System.out.println("Error closing file");
            }
        }
    }
}

finally without catch

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Try block");
            return;  // Exit method
        } finally {
            System.out.println("Finally still runs!");
        }
    }
}

Output:

Try block
Finally still runs!

finally with return

public class Main {
    static int test() {
        try {
            System.out.println("Try");
            return 1;
        } catch (Exception e) {
            System.out.println("Catch");
            return 2;
        } finally {
            System.out.println("Finally");
            // Executes before return
        }
    }

    public static void main(String[] args) {
        System.out.println("Returned: " + test());
    }
}

Output:

Try
Finally
Returned: 1

finally Overrides return

public class Main {
    static int test() {
        try {
            return 10;
        } finally {
            return 20;  // Overrides try's return
        }
    }

    public static void main(String[] args) {
        System.out.println(test());  // 20
    }
}

Warning: Don’t use return in finally - bad practice!


Complete Example: Database Connection

import java.sql.*;

class DatabaseHandler {
    void queryDatabase() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            // Connect to database
            System.out.println("Connecting to database...");
            // conn = DriverManager.getConnection(...);

            // Execute query
            System.out.println("Executing query...");
            // stmt = conn.createStatement();
            // rs = stmt.executeQuery("SELECT * FROM users");

            // Process results
            System.out.println("Processing results...");

        } catch (Exception e) {
            System.out.println("Database error: " + e.getMessage());
        } finally {
            // Close resources (cleanup)
            System.out.println("Cleaning up...");

            try {
                if (rs != null) rs.close();
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
                System.out.println("Resources closed");
            } catch (Exception e) {
                System.out.println("Error closing resources");
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        DatabaseHandler handler = new DatabaseHandler();
        handler.queryDatabase();
    }
}

Multiple Resources

import java.io.*;

public class Main {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader br = null;

        try {
            fr = new FileReader("input.txt");
            br = new BufferedReader(fr);

            String line = br.readLine();
            System.out.println(line);

        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            // Close in reverse order
            try {
                if (br != null) br.close();
                if (fr != null) fr.close();
            } catch (IOException e) {
                System.out.println("Error closing");
            }
        }
    }
}

finally Execution Flow

Scenario 1: No Exception
try → finally → continue

Scenario 2: Exception Caught
try → catch → finally → continue

Scenario 3: Exception Not Caught
try → finally → terminate

Scenario 4: return in try
try (return pending) → finally → return

Scenario 5: return in catch
catch (return pending) → finally → return

When finally Doesn’t Execute

Only these cases:

  1. System.exit()
try {
    System.exit(0);  // Terminates JVM
} finally {
    System.out.println("Won't execute");
}
  1. Fatal JVM Error
try {
    // JVM crashes
} finally {
    // Won't execute
}
  1. Infinite Loop
try {
    while (true) { }  // Never exits
} finally {
    System.out.println("Won't execute");
}

Nested finally

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Outer try");

            try {
                System.out.println("Inner try");
                int x = 10 / 0;
            } catch (ArithmeticException e) {
                System.out.println("Inner catch");
            } finally {
                System.out.println("Inner finally");
            }

        } catch (Exception e) {
            System.out.println("Outer catch");
        } finally {
            System.out.println("Outer finally");
        }
    }
}

Output:

Outer try
Inner try
Inner catch
Inner finally
Outer finally

Common Patterns

Pattern 1: Resource Cleanup

Resource resource = null;
try {
    resource = new Resource();
    resource.use();
} finally {
    if (resource != null) {
        resource.close();
    }
}

Pattern 2: Release Lock

Lock lock = new Lock();
try {
    lock.acquire();
    // Critical section
} finally {
    lock.release();
}

Pattern 3: Restore State

int oldValue = getValue();
try {
    setValue(newValue);
    // Do something
} finally {
    setValue(oldValue);  // Restore
}

try-finally vs try-catch-finally

try-finally:

// Use when you don't need to catch
try {
    // code
} finally {
    // cleanup
}
// Exception propagates

try-catch-finally:

// Use when you need to handle
try {
    // code
} catch (Exception e) {
    // handle
} finally {
    // cleanup
}

Best Practices

  1. Use for cleanup only
  2. Close resources in finally
  3. Don’t return from finally
  4. Don’t throw from finally (can hide exceptions)
  5. Keep it simple
  6. Check for null before closing
  7. Consider try-with-resources (Java 7+)

Quick Reference

// Basic pattern
try {
    // risky code
} catch (Exception e) {
    // handle
} finally {
    // cleanup (always runs)
}

// Without catch
try {
    // code
} finally {
    // cleanup
}

// Resource cleanup
Resource r = null;
try {
    r = new Resource();
    r.use();
} catch (Exception e) {
    // handle
} finally {
    if (r != null) {
        r.close();
    }
}

Common Mistakes

// ✗ Wrong - return in finally
try {
    return 1;
} finally {
    return 2;  // Bad! Hides try's return
}

// ✗ Wrong - throwing in finally
try {
    // code
} catch (Exception e) {
    // handle
} finally {
    throw new Exception();  // Hides original exception!
}

// ✗ Wrong - not checking null
Resource r = null;
try {
    r = new Resource();
} finally {
    r.close();  // NullPointerException if try fails!
}

// ✓ Correct - check null
Resource r = null;
try {
    r = new Resource();
} finally {
    if (r != null) {
        r.close();
    }
}

Exam Tips

Remember:

  1. finally always executes
  2. Used for cleanup
  3. Runs after try/catch
  4. Runs even with return
  5. Close resources in finally
  6. Can exist without catch
  7. Doesn’t run with System.exit()
  8. Don’t return from finally
  9. Check null before closing
  10. Runs before method returns

Common Questions:

  • What is finally block?
  • When does finally execute?
  • Does finally run with return?
  • Can finally exist without catch?
  • When finally doesn’t execute?
  • Use cases for finally?
  • finally with multiple returns?
  • Best practices?
  • Common mistakes?
  • finally vs try-with-resources?