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:
- System.exit()
try {
System.exit(0); // Terminates JVM
} finally {
System.out.println("Won't execute");
}
- Fatal JVM Error
try {
// JVM crashes
} finally {
// Won't execute
}
- 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
- Use for cleanup only
- Close resources in finally
- Don’t return from finally
- Don’t throw from finally (can hide exceptions)
- Keep it simple
- Check for null before closing
- 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:
- finally always executes
- Used for cleanup
- Runs after try/catch
- Runs even with return
- Close resources in finally
- Can exist without catch
- Doesn’t run with System.exit()
- Don’t return from finally
- Check null before closing
- 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?