When dealing with exceptions in Java, we frequently use the three keywords: try, catch, and finally. The try block wraps code that might throw exceptions, and the catch block captures and handles exceptions thrown in the try block. Today, we focus on the finally block, which has a special feature: the code in the finally block will execute regardless of whether an exception occurs in the try block or not, and regardless of whether the exception is caught or not. Doesn’t that sound useful?
一、Basic Syntax Structure¶
First, let’s recall the basic structure of try-catch-finally:
try {
// Code that may throw an exception
} catch (ExceptionType e) {
// Catch and handle the exception
} finally {
// Code that will execute regardless of exceptions
}
In this structure, the finally block is optional, but its execution logic is fixed: as long as the try block is executed (even if it contains only one line of code), the finally block will execute last, regardless of subsequent exceptions or whether they are caught.
二、Execution Scenarios of Finally Block¶
Let’s examine how the finally block behaves in different scenarios with examples.
1. try Block Executes Normally (No Exception)¶
When the code in the try block has no exceptions, the catch block is not executed, but the finally block still runs:
public class FinallyDemo1 {
public static void main(String[] args) {
try {
System.out.println("try block: This line will execute normally");
} catch (Exception e) {
System.out.println("catch block: This won't execute (no exception occurred)");
} finally {
System.out.println("finally block: I will execute no matter what!");
}
}
}
Output:
try block: This line will execute normally
finally block: I will execute no matter what!
2. try Block Throws an Exception, Which Is Caught by catch¶
When the code in the try block throws an exception, the catch block handles it, and then the finally block still executes:
public class FinallyDemo2 {
public static void main(String[] args) {
try {
int a = 10 / 0; // This throws ArithmeticException (division by zero)
System.out.println("try block: This line won't execute (exception already occurred)");
} catch (ArithmeticException e) {
System.out.println("catch block: Caught exception: " + e.getMessage());
} finally {
System.out.println("finally block: I executed, and the exception was handled!");
}
}
}
Output:
catch block: Caught exception: / by zero
finally block: I executed, and the exception was handled!
3. try Block Throws an Exception Without a catch Block¶
Even if there is no catch block, as long as the try block is executed, the finally block will still run. The exception will then propagate upward (causing the program to terminate):
public class FinallyDemo3 {
public static void main(String[] args) {
try {
int b = 20 / 0; // Throws ArithmeticException (division by zero)
} finally {
System.out.println("finally block: I will definitely execute!");
}
// This line will not execute because the exception propagates upward
}
}
Output:
finally block: I will definitely execute!
Exception in thread "main" java.lang.ArithmeticException: / by zero
Note: Even without a catch block, the finally block executes, but the exception continues to be thrown to the upper-level code, terminating the program.
三、Why Use the Finally Block? Most Common Use Case: Resource Release¶
You might ask: “What is the finally block actually used for?” The most typical scenario is releasing resources, such as:
- Closing files, database connections, network connections;
- Releasing memory, thread locks, etc.
If these resources are not actively closed, they can lead to resource leaks (e.g., files remain open and cannot be accessed by other programs). The finally block ensures that resource release code runs regardless of whether the try block succeeds or fails.
Example of file closing:
import java.io.FileReader;
import java.io.IOException;
public class FileResourceExample {
public static void readFile() {
FileReader fr = null; // Declare file stream and initialize to null
try {
fr = new FileReader("test.txt"); // Open file (may throw an error)
int data = fr.read(); // Read file data (may throw an error)
System.out.println("Data read: " + data);
} catch (IOException e) {
System.out.println("Error in file operation: " + e.getMessage());
} finally {
// Ensure the file stream is closed regardless of errors
if (fr != null) {
try {
fr.close(); // Close the file stream
System.out.println("File stream closed");
} catch (IOException e) {
e.printStackTrace(); // Handle closing errors
}
}
}
}
}
Here, fr.close() in the finally block ensures the file stream is closed whether the file read succeeds or fails, preventing resource waste.
四、Caution: finally Block May “Override” Return Values¶
If the try block contains a return statement, the return in the finally block will override the return value in the try block (though this is not recommended due to ambiguity):
public class FinallyReturnExample {
public static int test() {
try {
return 1; // Temporarily assume return 1
} finally {
return 2; // But the finally block's return overrides the above return
}
}
public static void main(String[] args) {
System.out.println(test()); // Output: 2
}
}
Output:
2
This happens because the finally block has the highest execution priority. It executes after the try block’s return and directly returns its own return value.
五、Summary: Core Features of the Finally Block¶
- Guaranteed Execution: As long as the
tryblock is executed (even with one line of code), thefinallyblock runs, regardless of whether exceptions are caught. - Resource Release: Commonly used to close files, database connections, etc., to prevent resource leaks.
- Execution Priority: If both
tryandfinallyhavereturn, thefinallyblock’sreturnoverrides thetryblock’sreturn.
Mastering the finally block makes your Java code more robust—ensuring critical cleanup operations (e.g., closing resources) are never missed, even when exceptions occur. It’s a crucial part of Java exception handling!