Basic Syntax and Behavior
public class FinallyExample {
public static void main(String[] args) {
try {
System.out.println("Inside try block");
int result = 10 / 2; // No exception
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Exception caught: " + e.getMessage());
} finally {
System.out.println("Finally block executed - always runs!");
}
}
}
Output:
Inside try block Result: 5 Finally block executed - always runs!
Finally with Exception
public class FinallyWithException {
public static void main(String[] args) {
try {
System.out.println("Inside try block");
int result = 10 / 0; // ArithmeticException
System.out.println("This won't be printed");
} catch (ArithmeticException e) {
System.out.println("Exception caught: " + e.getMessage());
} finally {
System.out.println("Finally block executed despite exception!");
}
}
}
Output:
Inside try block Exception caught: / by zero Finally block executed despite exception!
Finally with Uncaught Exception
public class FinallyUncaughtException {
public static void main(String[] args) {
try {
System.out.println("Inside try block");
String str = null;
str.length(); // NullPointerException
} catch (ArithmeticException e) {
System.out.println("This won't catch NullPointerException");
} finally {
System.out.println("Finally executes even with uncaught exception!");
}
// This line won't be reached due to uncaught exception
}
}
Output:
Inside try block Finally executes even with uncaught exception! Exception in thread "main" java.lang.NullPointerException
Return Statement in Finally
public class FinallyReturn {
public static int testFinallyReturn() {
try {
System.out.println("Inside try");
return 1;
} finally {
System.out.println("Inside finally");
return 2; // This overrides the try return!
}
}
public static void main(String[] args) {
System.out.println("Return value: " + testFinallyReturn());
}
}
Output:
Inside try Inside finally Return value: 2
System.exit() in Finally
public class FinallySystemExit {
public static void testSystemExit() {
try {
System.out.println("Inside try");
System.exit(0); // JVM terminates here
} finally {
System.out.println("This won't be printed!");
}
}
public static void main(String[] args) {
testSystemExit();
System.out.println("This also won't be printed");
}
}
Output:
Inside try
Real-World Use Cases
1. Resource Cleanup
import java.io.*;
public class ResourceCleanup {
public static void readFile(String filename) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
} finally {
try {
if (reader != null) {
reader.close();
System.out.println("File closed successfully");
}
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
}
}
2. Database Connection Cleanup
public class DatabaseExample {
public static void processDatabase() {
Connection conn = null;
PreparedStatement stmt = null;
try {
// Get database connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "pass");
stmt = conn.prepareStatement("SELECT * FROM users");
// Execute query and process results
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
// Process each row
}
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
} finally {
// Always close resources
try {
if (stmt != null) stmt.close();
if (conn != null) conn.close();
System.out.println("Database resources closed");
} catch (SQLException e) {
System.out.println("Error closing resources: " + e.getMessage());
}
}
}
}
Try-with-Resources (Java 7+)
import java.io.*;
public class TryWithResources {
public static void readFileModern(String filename) {
// Automatic resource management - no finally needed for closing
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
// Resources automatically closed here
}
}
Complex Scenarios
Nested Finally Blocks
public class NestedFinally {
public static void complexOperation() {
try {
System.out.println("Outer try");
try {
System.out.println("Inner try");
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Inner catch");
throw e; // Re-throw exception
} finally {
System.out.println("Inner finally");
}
} catch (Exception e) {
System.out.println("Outer catch: " + e.getMessage());
} finally {
System.out.println("Outer finally");
}
}
public static void main(String[] args) {
complexOperation();
}
}
Output:
Outer try Inner try Inner catch Inner finally Outer catch: / by zero Outer finally
Important Rules and Behaviors
1. Execution Order
public class ExecutionOrder {
public static String testOrder() {
try {
System.out.println("Try block");
return "Try return";
} catch (Exception e) {
System.out.println("Catch block");
return "Catch return";
} finally {
System.out.println("Finally block");
// If you return here, it overrides previous returns
}
}
public static void main(String[] args) {
System.out.println("Result: " + testOrder());
}
}
Output:
Try block Finally block Result: Try return
2. Exception in Finally Block
public class ExceptionInFinally {
public static void testFinallyException() {
try {
try {
System.out.println("Inner try");
throw new RuntimeException("Exception from try");
} finally {
System.out.println("Inner finally");
throw new RuntimeException("Exception from finally");
}
} catch (RuntimeException e) {
System.out.println("Caught: " + e.getMessage());
}
}
public static void main(String[] args) {
testFinallyException();
}
}
Output:
Inner try Inner finally Caught: Exception from finally
Best Practices
- Use finally for cleanup - Always close resources in finally blocks
- Avoid returns in finally - They can suppress exceptions from try/catch
- Keep finally blocks simple - Avoid complex logic that might throw exceptions
- Use try-with-resources - For automatic resource management when possible
- Handle exceptions in finally - Wrap resource cleanup in try-catch within finally
// ✅ Good practice
public static void safeResourceUsage() {
Connection conn = null;
try {
conn = getConnection();
// Use connection
} catch (SQLException e) {
// Handle exception
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// Log but don't throw - don't mask original exception
System.err.println("Warning: Failed to close connection: " + e.getMessage());
}
}
}
}
The finally block is crucial for ensuring that cleanup code always executes, regardless of what happens in the try-catch blocks, making it essential for robust resource management and preventing resource leaks.