CodeGym /課程 /JAVA 25 SELF /進階的例外處理與最佳實務

進階的例外處理與最佳實務

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

1. 多個 catch:處理不同的例外

在真實的程式中,同一段程式碼可能會拋出不同型別的例外。舉例來說,讀取檔案時可能出現 FileNotFoundExceptionIOException,而在剖析資料時——還可能出現 NumberFormatException。為了正確處理每種情況,Java 可以連續使用多個 catch 區塊。

語法

try {
    // 危險操作
} catch (FileNotFoundException e) {
    System.out.println("找不到檔案:" + e.getMessage());
} catch (IOException e) {
    System.out.println("輸入/輸出錯誤:" + e.getMessage());
} catch (NumberFormatException e) {
    System.out.println("數值格式不正確:" + e.getMessage());
}

重點:
catch 區塊必須由較具體排到較一般! 如果一開始就寫 catch (Exception e),其他更具體的區塊將永遠到達不了,編譯器會報錯。

為什麼這很重要?

  • 特定處理: 可以針對不同錯誤採取不同反應(例如提示選擇其他檔案或重新輸入)。
  • 可讀性: 程式碼更清楚——看得出預期會有什麼錯誤以及如何處理。

2. 具有多個型別的 catch(multi-catch)

有時不同的例外需要相同的處理。比方說,不論是讀檔錯誤或是數字轉換錯誤,你只想輸出錯誤訊息或記錄日誌。

自 Java 7 起,提供了 multi-catch 語法,允許在同一個區塊中合併多個例外型別:

try {
    // 危險操作
} catch (IOException | NumberFormatException e) {
    System.out.println("處理檔案或數字時發生錯誤:" + e.getMessage());
}

運作方式:

  • 透過垂直線(|)列出例外型別。
  • 變數 e 會具有這些型別的共同父型別(通常是 Exception)。
  • 在區塊內不能重新指定變數 e 的值——它會自動被視為 final

範例:

try {
    BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("數字:" + number);
    reader.close();
} catch (IOException | NumberFormatException e) {
    System.out.println("錯誤:" + e.getMessage());
}

3. 例外處理最佳實務

不要吞掉例外

不好:

try {
    // 某些危險操作
} catch (Exception e) {
    // 什麼都不做!
}

為什麼不好?
空的 catch 區塊會「吞掉」錯誤——你會失去故障原因的資訊,程式將變得難以除錯。

較好: 至少輸出訊息或記錄錯誤。

catch (Exception e) {
    e.printStackTrace();
}

如果你無法在此處處理例外——就把它往外拋:

catch (IOException e) {
    throw e; // 或是 throw new RuntimeException(e);
}

拋出最具體的例外

當你撰寫自己的方法或 API 時,務必拋出最具體的例外,而不是單純的 ExceptionRuntimeException。這能讓你的程式碼與介面對其他開發者更清楚。

範例:

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("餘額不足");
    }
    // ...
}

不要用例外來控制程式流程

例外是為了處理例外情況——也就是在正常執行流程中不應該發生的錯誤。不要把例外用在一般邏輯(例如用來跳出迴圈或檢查條件)。

不好:

try {
    while (true) {
        String line = reader.readLine();
        if (line == null) break;
        // 處理每一行
    }
} catch (Exception e) {
    // 用例外來跳出迴圈——很糟!
}

好:

String line;
while ((line = reader.readLine()) != null) {
    // 處理每一行
}

4. 實務範例

範例:針對不同錯誤做不同處理

try {
    BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("數字:" + number);
    reader.close();
} catch (FileNotFoundException e) {
    System.out.println("找不到檔案!");
} catch (IOException e) {
    System.out.println("讀取檔案時發生錯誤!");
} catch (NumberFormatException e) {
    System.out.println("檔案內容不是數字!");
}

範例:multi-catch 進行相同處理

try {
    // 某些危險操作
} catch (IOException | NumberFormatException e) {
    System.out.println("錯誤:" + e.getMessage());
}

5. 重要細節與常見錯誤

錯誤 №1:catch 區塊順序不正確。 應先寫較具體的例外,然後再寫一般的。否則永遠不會執行到較廣泛的區塊。

錯誤 №2:在 multi-catch 中使用具繼承關係的型別。 例如,catch (IOException | Exception e) 不會通過編譯,因為 IOExceptionException 的子類別。

錯誤 №3:嘗試在 multi-catch 中修改變數 e。 在這種情況下 e 會被視為 final——不能重新指派新值。

錯誤 №4:空的 catch 區塊。 忽略錯誤會導致「無聲」的 bug。至少要輸出訊息或記錄例外。

錯誤 №5:一股腦地捕捉所有 Exception。 使用 catch (Exception e) 會掩蓋真正的問題並讓除錯更困難。最好只捕捉預期的例外。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION