CodeGym /課程 /JAVA 25 SELF /finally 與 throw:收尾與拋出例外

finally 與 throw:收尾與拋出例外

JAVA 25 SELF
等級 11 , 課堂 3
開放

1. 認識 finally 區塊

當你處理資源——檔案、網路連線、資料庫——必須確保它們會被關閉被釋放無論如何都會發生,即便在執行過程中發生錯誤。Java 為此提供了特別的區塊——finally

finally 如何運作?

區塊 finally 是結構 try-catch-finally 的一部分。只要寫了 finally,裡面的程式碼就會一律執行——不論是否發生例外。即使在 try 裡有 return 或拋出例外,finally 仍然會執行(除非電腦被關機,或程式被 System.exit(0) 強制終止)。

語法:

try {
    // 可能拋出例外的程式碼
} catch (ExceptionType e) {
    // 錯誤處理
} finally {
    // 這段程式碼一定會執行!
}

範例

try {
    System.out.println("開始執行");
    int result = 10 / 0; // 這裡會發生錯誤
    System.out.println("結果: " + result);
} catch (ArithmeticException e) {
    System.out.println("錯誤:除以零");
} finally {
    System.out.println("無論如何這段程式碼都會執行");
}

執行結果:

開始執行
錯誤:除以零
無論如何這段程式碼都會執行

發生了什麼事?

  1. try 中我們嘗試做除法,結果發生錯誤。
  2. 若除法發生錯誤——catch 會處理它。
  3. 但是! 不管怎樣,finally 都會執行,並在主控台輸出訊息。

2. 只有 finally(沒有 catch)

這個結構有 3 種寫法:

  • 完整:try-catch-finally
  • 沒有 finallytry-catch
  • 沒有 catchtry-finally

第三種用法適用於把攔截與處理錯誤留給上層方法。但需要區塊 finally 來保證某些程式碼一定執行:

  • 關閉檔案、網路連線、資料庫。
  • 釋放各種資源(例如鎖)。
  • 記錄日誌:寫入操作完成的資訊。

範例:

try {
    System.out.println("進行除法運算");
    int result = 10 / 0;   // 錯誤!
    System.out.println("結果: " + result);
} finally {
    System.out.println("finally 區塊已執行");
}

結果:

進行除法運算
finally 區塊已執行
Exception in thread "main" java.lang.ArithmeticException: / by zero

什麼情況下 finally 不會執行?

幾乎一定會執行。例外情況如下:

  1. 使用:System.exit(0) 強制結束程式。
  2. 執行 finally 的執行緒被強制終止。
  3. 電腦被關機。

3. throw 運算子:如何自行拋出例外

有時候 Java 會自行「拋出」例外(例如除以零、陣列越界)。但有些情況下,需要明確表示:「這是錯誤!我無法繼續執行!」。為此 Java 提供了運算子 throw

比喻: 如果你在商店看到過期商品——你會提出申訴。程式碼也是如此:只要有不合理的狀況——就拋出例外。

throw 的語法

throw new ExceptionType("錯誤訊息");

ExceptionType —— 任何繼承自 Throwable 的類別(通常是 ExceptionRuntimeException)。括號中的訊息有助於了解問題出在哪裡。

範例:方法引數檢查

public static int safeDivide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("除數不可為零");
    }
    return a / b;
}

使用方式:

public static void main(String[] args) {
    try {
        int result = safeDivide(10, 0);
        System.out.println("結果: " + result);
    } catch (IllegalArgumentException e) {
        System.out.println("錯誤: " + e.getMessage());
    }
}

結果:

錯誤:除數不可為零

什麼時候要用 throw?

  • 方法引數檢查(例如收到 null 或不正確的資料)。
  • 物件狀態檢查(例如嘗試從餘額為 0 歐元的帳戶提款)。
  • catch 裡——若想把例外「再丟出去」(例如加入更多資訊)。

4. 結合使用 try-catch-finally 與 throw

有時這些結構會一起運作。比如你先捕捉到一個錯誤,接著決定拋出一個更具資訊性的自訂例外。

public static int parseAndDivide(String text, int divisor) {
    try {
        int number = Integer.parseInt(text);
        if (divisor == 0) {
            throw new IllegalArgumentException("除數不可為零");
        }
        return number / divisor;
    } catch (NumberFormatException e) {
        throw new IllegalArgumentException("字串 '" + text + "' 不是數字");
    } finally {
        System.out.println("嘗試處理字串: " + text);
    }
}

使用方式:

try {
    int result = parseAndDivide("42a", 2);
    System.out.println("結果: " + result);
} catch (IllegalArgumentException e) {
    System.out.println("錯誤: " + e.getMessage());
}

結果:

嘗試處理字串: 42a
錯誤:字串 '42a' 不是數字

重要細節:return 與 finally

即使在區塊 try 裡有 returnfinally 仍然會執行!

public static int getValue() {
    try {
        return 10;
    } finally {
        System.out.println("finally 仍然會執行!");
    }
}

呼叫 getValue() 會輸出:

finally 仍然會執行!

5. 使用 finally 與 throw 的常見錯誤

錯誤一:未用 finally 關閉資源。
這是很常見的問題:開啟檔案卻沒有關閉——導致資源外洩。務必使用 finally(或稍後會談的 try-with-resources)。

錯誤二:拋出例外卻沒有處理。
如果你用 throw 拋出例外,卻沒有任何地方捕捉(沒有 try-catch),程式將非正常終止。務必思考由誰來捕捉你的例外。

錯誤三:在 finally 中使用 return。
如果你不小心在 finally 裡寫了 return,它會「覆蓋」所有先前的 returnthrow。這可能導致非常難以察覺的 Bug。強烈不建議這麼做!

public int tricky() {
    try {
        return 1;
    } finally {
        return 2; // 危險:會回傳 2,而不是 1!
    }
}

結果: 最後會回傳 2,儘管在 try 中是 1

錯誤四:遺失例外資訊。
如果你捕捉了一個例外,接著又拋出新的例外,卻未保留舊例外(e)的資訊,你會遺失呼叫堆疊,進而使除錯困難。較好的寫法是:

catch (NumberFormatException e) {
    throw new IllegalArgumentException("轉換錯誤", e);
}
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION