Finally Block in Java
Learn how to execute code that always runs, no matter what
💡 Think of finally like always washing your hands after eating - no matter what happens during the meal (you drop your food, you finish early, you spill something), you ALWAYS wash your hands at the end! Finally always executes!
🔒 What is Finally?
The finally block is a section of code that always executes after a try-catch block, regardless of whether an exception was thrown or caught. It's perfect for cleanup tasks like closing files or database connections.
public class BasicFinally { public static void main(String[] args) { System.out.println("Program started"); try { System.out.println("1. Inside try block"); int result = 10 / 2; // No exception System.out.println("2. Result: " + result); } catch (ArithmeticException e) { System.out.println("3. Inside catch block"); System.out.println(" Error: " + e.getMessage()); } finally { // This ALWAYS runs! System.out.println("4. Inside finally block"); System.out.println(" This always executes!"); } System.out.println("5. Program ended"); }}// Output:// Program started// 1. Inside try block// 2. Result: 5// 4. Inside finally block// This always executes!// 5. Program ended// Note: catch didn't run because no exception occurred,// but finally STILL ran!🔍 📋 Execution Order
Understanding when finally executes:
Try Block Runs
Code in try block starts executing
Exception or Success
Either an exception occurs or code completes normally
Catch Block (if exception)
Catch block handles the exception if one occurred
Finally ALWAYS Runs
Finally block executes no matter what happened above
public class ExecutionOrder { public static void main(String[] args) { System.out.println("=== Test 1: No Exception ==="); testFinally(10, 2); System.out.println("\n=== Test 2: With Exception ==="); testFinally(10, 0); } public static void testFinally(int a, int b) { try { System.out.println("Step 1: Try block starts"); int result = a / b; System.out.println("Step 2: Result = " + result); } catch (ArithmeticException e) { System.out.println("Step 3: Catch block"); System.out.println(" Caught: " + e.getMessage()); } finally { System.out.println("Step 4: Finally block"); System.out.println(" This ALWAYS runs!"); } System.out.println("Step 5: After try-catch-finally"); }}// Output:// === Test 1: No Exception ===// Step 1: Try block starts// Step 2: Result = 5// Step 4: Finally block// This ALWAYS runs!// Step 5: After try-catch-finally//// === Test 2: With Exception ===// Step 1: Try block starts// Step 3: Catch block// Caught: / by zero// Step 4: Finally block// This ALWAYS runs!// Step 5: After try-catch-finally📁 Real-World Example: File Handling
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class FileHandling { public static void main(String[] args) { readFile("data.txt"); } public static void readFile(String filename) { BufferedReader reader = null; try { System.out.println("Opening file: " + filename); reader = new BufferedReader(new FileReader(filename)); String line; int lineNumber = 1; while ((line = reader.readLine()) != null) { System.out.println("Line " + lineNumber + ": " + line); lineNumber++; } } catch (IOException e) { System.out.println("Error reading file: " + e.getMessage()); } finally { // Cleanup: ALWAYS close the file System.out.println("\nCleaning up..."); if (reader != null) { try { reader.close(); System.out.println("File closed successfully!"); } catch (IOException e) { System.out.println("Error closing file: " + e.getMessage()); } } } System.out.println("Operation complete!"); }}// Key points:// 1. Finally ensures file is closed even if error occurs// 2. Prevents resource leaks// 3. Cleanup code runs whether exception occurred or not// 4. This is a common pattern for resource management💾 Real-World Example: Database Connection
// Simulated database connection exampleclass DatabaseConnection { private String connectionString; private boolean isOpen = false; public DatabaseConnection(String connectionString) { this.connectionString = connectionString; } public void open() throws Exception { System.out.println("Opening connection: " + connectionString); if (connectionString.equals("invalid")) { throw new Exception("Invalid connection string!"); } isOpen = true; System.out.println("Connection opened!"); } public void executeQuery(String query) { System.out.println("Executing: " + query); if (query.contains("ERROR")) { throw new RuntimeException("Query failed!"); } System.out.println("Query successful!"); } public void close() { if (isOpen) { System.out.println("Closing connection..."); isOpen = false; System.out.println("Connection closed!"); } }}public class DatabaseExample { public static void main(String[] args) { System.out.println("=== Test 1: Successful Query ==="); performQuery("localhost:3306", "SELECT * FROM users"); System.out.println("\n=== Test 2: Failed Query ==="); performQuery("localhost:3306", "SELECT * ERROR FROM users"); System.out.println("\n=== Test 3: Connection Failed ==="); performQuery("invalid", "SELECT * FROM users"); } public static void performQuery(String connectionString, String query) { DatabaseConnection conn = new DatabaseConnection(connectionString); try { conn.open(); conn.executeQuery(query); } catch (Exception e) { System.out.println("Error: " + e.getMessage()); } finally { // ALWAYS close the connection System.out.println("\n[Finally Block]"); conn.close(); } System.out.println("Done!\n"); }}// Output:// === Test 1: Successful Query ===// Opening connection: localhost:3306// Connection opened!// Executing: SELECT * FROM users// Query successful!//// [Finally Block]// Closing connection...// Connection closed!// Done!//// === Test 2: Failed Query ===// Opening connection: localhost:3306// Connection opened!// Executing: SELECT * ERROR FROM users// Error: Query failed!//// [Finally Block]// Closing connection...// Connection closed!// Done!//// === Test 3: Connection Failed ===// Opening connection: invalid// Error: Invalid connection string!//// [Finally Block]// Done!🔄 Finally with Return Statements
public class FinallyWithReturn { public static void main(String[] args) { System.out.println("Result 1: " + testReturn1()); System.out.println("\nResult 2: " + testReturn2()); System.out.println("\nResult 3: " + testReturn3()); } // Example 1: Return in try public static int testReturn1() { System.out.println("Test 1 started"); try { System.out.println(" In try block"); return 10; // Method will return, but... } finally { System.out.println(" In finally block"); System.out.println(" Finally runs BEFORE return!"); } // Note: Finally executes before the return value is sent back } // Example 2: Return in catch public static int testReturn2() { System.out.println("Test 2 started"); try { System.out.println(" In try block"); int x = 10 / 0; // Exception! return 10; } catch (ArithmeticException e) { System.out.println(" In catch block"); return 20; } finally { System.out.println(" In finally block"); System.out.println(" Finally runs even with return in catch!"); } } // Example 3: Return in finally (DON'T DO THIS!) public static int testReturn3() { System.out.println("Test 3 started"); try { System.out.println(" In try block"); return 10; } finally { System.out.println(" In finally block"); return 99; // BAD PRACTICE: This overwrites try's return! } }}// Output:// Test 1 started// In try block// In finally block// Finally runs BEFORE return!// Result 1: 10//// Test 2 started// In try block// In catch block// In finally block// Finally runs even with return in catch!// Result 2: 20//// Test 3 started// In try block// In finally block// Result 3: 99// Important: Finally's return (99) overwrites try's return (10)!// This is confusing and should be avoided!⚡ Try-Finally Without Catch
public class TryFinally { public static void main(String[] args) { System.out.println("=== Test 1: No Exception ==="); test1(); System.out.println("\n=== Test 2: With Exception ==="); try { test2(); } catch (Exception e) { System.out.println("Main caught: " + e.getMessage()); } } // You can use try-finally without catch! public static void test1() { System.out.println("Method started"); try { System.out.println("Doing some work..."); int result = 10 / 2; System.out.println("Result: " + result); } finally { System.out.println("Cleanup in finally"); } System.out.println("Method ended"); } public static void test2() { System.out.println("Method started"); try { System.out.println("Doing some work..."); int result = 10 / 0; // Exception! System.out.println("Result: " + result); } finally { // This runs even though exception isn't caught here System.out.println("Cleanup in finally"); } // This line never executes System.out.println("Method ended"); }}// Output:// === Test 1: No Exception ===// Method started// Doing some work...// Result: 5// Cleanup in finally// Method ended//// === Test 2: With Exception ===// Method started// Doing some work...// Cleanup in finally// Main caught: / by zero// Key point: Finally runs even when exception isn't caught locally!// The exception propagates up after finally executes.🎭 Nested Try-Catch-Finally
public class NestedTryCatch { public static void main(String[] args) { System.out.println("Program started"); try { System.out.println("\n1. Outer try"); try { System.out.println(" 2. Inner try"); int result = 10 / 0; // Exception! System.out.println(" This won't print"); } catch (ArithmeticException e) { System.out.println(" 3. Inner catch: " + e.getMessage()); } finally { System.out.println(" 4. Inner finally - always runs"); } System.out.println("5. After inner try-catch-finally"); // Another operation String text = null; System.out.println(text.length()); // NullPointerException! } catch (NullPointerException e) { System.out.println("6. Outer catch: " + e.getClass().getSimpleName()); } finally { System.out.println("7. Outer finally - always runs"); } System.out.println("\n8. Program ended"); }}// Output:// Program started//// 1. Outer try// 2. Inner try// 3. Inner catch: / by zero// 4. Inner finally - always runs// 5. After inner try-catch-finally// 6. Outer catch: NullPointerException// 7. Outer finally - always runs//// 8. Program ended// Both finally blocks execute!// Inner finally runs when inner try-catch completes// Outer finally runs when outer try-catch completes🆕 Modern Alternative: Try-With-Resources
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class TryWithResources { public static void main(String[] args) { System.out.println("=== Old Way (with finally) ==="); readFileOldWay("data.txt"); System.out.println("\n=== New Way (try-with-resources) ==="); readFileNewWay("data.txt"); } // Old way: Manual cleanup in finally public static void readFileOldWay(String filename) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(filename)); String line = reader.readLine(); System.out.println("Read: " + line); } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } finally { // Manual cleanup - lots of code! if (reader != null) { try { reader.close(); System.out.println("File closed in finally"); } catch (IOException e) { System.out.println("Error closing: " + e.getMessage()); } } } } // New way: Automatic cleanup! public static void readFileNewWay(String filename) { // Resources declared here are automatically closed try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { String line = reader.readLine(); System.out.println("Read: " + line); // No finally needed! Resource closes automatically } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } System.out.println("File auto-closed!"); }}// Try-with-resources benefits:// 1. Shorter, cleaner code// 2. Automatic resource cleanup// 3. Proper exception handling// 4. Works with any AutoCloseable resource// 5. Multiple resources can be declared// Syntax:// try (Resource r1 = new Resource(); Resource r2 = new Resource()) {// // use resources// } // resources closed automatically🔑 Key Concepts
Always Executes
Finally runs whether exception occurs or not
Perfect for cleanup code that must always run
Resource Cleanup
Close files, database connections, network sockets
finally { file.close(); }
Optional
You don't need finally if you don't need cleanup
try-catch is valid without finally
Execution Guarantee
Executes even if return statement in try/catch
Finally runs before method returns
✨ Best Practices
- ✓Use finally for resource cleanup (closing files, connections)
- ✓Don't put return statements in finally block (confusing!)
- ✓Don't throw exceptions from finally (overwrites original exception)
- ✓Consider try-with-resources for automatic resource management
- ✓Keep finally blocks short and focused on cleanup
- ✓Avoid complex logic in finally blocks
💼 Interview Tips
- •Finally always executes, even with return statements in try/catch
- •Finally executes before method returns
- •If both catch and finally have return, finally's return wins
- •Finally executes even if exception is not caught
- •Understand try-with-resources as modern alternative
- •Know that finally doesn't execute if JVM crashes or System.exit()