Java異常finally塊:無論是否異常,這段代碼總會執行

我們在使用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塊的核心特點

  1. 必執行性:只要try塊被執行(哪怕只有一行代碼),finally塊就會執行,與是否捕獲異常無關。
  2. 資源釋放:常用於關閉文件、數據庫連接等資源,避免資源泄漏。
  3. 執行優先級:如果try和finally都有return,finally的return會覆蓋try的return。

掌握finally塊的用法,能讓你的Java代碼更健壯——即使出現異常,也能確保關鍵的收尾操作(比如關閉資源)不會被遺漏。這是Java異常處理中非常重要的一環!

小夜