我們在使用Java處理異常的時候,經常會用到try、catch和finally這三個關鍵詞。其中,try塊用來包裹可能會出現異常的代碼,catch塊用來捕獲和處理try塊中發生的異常。而今天我們要重點說的finally塊,它有個特別的特點——無論try塊有沒有異常發生,無論異常有沒有被catch到,finally塊裏的代碼都會執行。這聽起來是不是很有用?
一、基本語法結構¶
首先,我們先回憶一下try-catch-finally的基本結構:
try {
// 可能出現異常的代碼
} catch (異常類型 e) {
// 捕獲並處理異常
} finally {
// 無論是否異常,都會執行的代碼
}
在這個結構中,finally塊是可選的,但它的執行邏輯是固定的:只要try塊被執行(哪怕try裏只有一行代碼),不管後續有沒有異常或者異常是否被捕獲,finally塊的代碼都會“最後執行”。
二、不同場景下的finally執行情況¶
我們通過幾個例子來看看finally塊到底是怎麼執行的。
1. try塊正常執行(無異常)¶
當try塊裏的代碼沒有異常時,catch塊不會執行,但finally塊依然會執行:
public class FinallyDemo1 {
public static void main(String[] args) {
try {
System.out.println("try塊:這行代碼會正常執行");
} catch (Exception e) {
System.out.println("catch塊:這裏不會執行(因爲沒有異常)");
} finally {
System.out.println("finally塊:無論是否異常,我都會執行!");
}
}
}
輸出結果:
try塊:這行代碼會正常執行
finally塊:無論是否異常,我都會執行!
2. try塊有異常,且被catch捕獲¶
當try塊裏的代碼拋出異常時,catch塊會處理異常,之後finally塊依然會執行:
public class FinallyDemo2 {
public static void main(String[] args) {
try {
int a = 10 / 0; // 這行會拋出ArithmeticException(除零異常)
System.out.println("try塊:這行代碼不會執行(異常已經發生)");
} catch (ArithmeticException e) {
System.out.println("catch塊:捕獲到異常:" + e.getMessage());
} finally {
System.out.println("finally塊:我被執行了,異常已被處理!");
}
}
}
輸出結果:
catch塊:捕獲到異常:/ by zero
finally塊:我被執行了,異常已被處理!
3. try塊有異常,但沒有catch塊¶
即使沒有catch塊,只要try塊被執行,finally塊依然會執行,然後異常會繼續向上傳播(導致程序終止):
public class FinallyDemo3 {
public static void main(String[] args) {
try {
int b = 20 / 0; // 拋出ArithmeticException(除零異常)
} finally {
System.out.println("finally塊:我一定會執行!");
}
// 這裏不會執行,因爲異常會向上傳播
}
}
輸出結果:
finally塊:我一定會執行!
Exception in thread "main" java.lang.ArithmeticException: / by zero
注意:即使沒有catch塊,finally塊依然會執行,只是異常會繼續拋出到上層代碼,導致程序終止。
三、爲什麼需要finally塊?最常見的用途——資源釋放¶
你可能會問:“既然finally塊這麼重要,那它到底用來幹嘛呢?”最典型的場景是釋放資源,比如:
- 關閉文件、數據庫連接、網絡連接;
- 釋放內存、線程鎖等。
這些資源如果不主動關閉,會導致系統資源泄漏(比如文件一直佔用,無法被其他程序訪問)。而finally塊能確保無論try塊是否出錯,資源釋放的代碼都會執行。
舉個文件關閉的例子:
import java.io.FileReader;
import java.io.IOException;
public class FileResourceExample {
public static void readFile() {
FileReader fr = null; // 聲明文件流,初始化爲null
try {
fr = new FileReader("test.txt"); // 打開文件(可能出錯)
int data = fr.read(); // 讀取文件數據(可能出錯)
System.out.println("讀取到的數據:" + data);
} catch (IOException e) {
System.out.println("文件操作出錯:" + e.getMessage());
} finally {
// 確保文件流被關閉,無論是否出錯
if (fr != null) {
try {
fr.close(); // 關閉文件流
System.out.println("文件流已關閉");
} catch (IOException e) {
e.printStackTrace(); // 關閉時出錯也處理一下
}
}
}
}
}
這裏,無論文件讀取是否出錯,fr.close()都會在finally塊中執行,確保文件流被關閉,避免資源浪費。
四、注意:finally塊可能“覆蓋”return值¶
如果try塊中有return語句,finally塊的return會“覆蓋”try塊的return值(但這種情況不推薦,容易引發歧義):
public class FinallyReturnExample {
public static int test() {
try {
return 1; // 先假設返回1
} finally {
return 2; // 但finally塊的return會覆蓋上面的return
}
}
public static void main(String[] args) {
System.out.println(test()); // 輸出結果是2
}
}
輸出結果:
2
這是因爲finally塊的執行優先級最高,會在try塊的return之後執行,並直接返回自己的return值。
五、總結:finally塊的核心特點¶
- 必執行性:只要try塊被執行(哪怕只有一行代碼),finally塊就會執行,與是否捕獲異常無關。
- 資源釋放:常用於關閉文件、數據庫連接等資源,避免資源泄漏。
- 執行優先級:如果try和finally都有return,finally的return會覆蓋try的return。
掌握finally塊的用法,能讓你的Java代碼更健壯——即使出現異常,也能確保關鍵的收尾操作(比如關閉資源)不會被遺漏。這是Java異常處理中非常重要的一環!