Java 獨特的 異常規範 機制(throws 子句)強制方法宣告可能拋出的已檢查異常,讓編譯器在建構期就能驗證異常處理的完整性。這比 C++ 的執行期檢查更早發現問題。本文將詳細說明語法規則、編譯器強制邏輯、RuntimeException 的特例,以及實務上的設計考量。
方法宣告中,參數列表之後、方法主體之前,以 throws 列出可能拋出的已檢查異常:
void f() throws TooBigException, TooSmallException, DivZeroException {
// 方法本體
}
Exception 但非 RuntimeException)throws 子句Java 採取「處理或宣告」原則:
throw new IOException())且未以 try-catch 捕獲,必須在方法簽名加上 throws IOExceptionmain() 的異常傳遞鏈,編譯器逐層驗證這是相較 C++ 最大的優勢:C++ 異常規範違反只在執行期發現,Java 則在編譯期攔截。
RuntimeException 及其子類不需出現在 throws 子句中,原因有二:
但你仍可選擇在 throws 中列出 RuntimeException 以文件化意圖,編譯器允許但不強制。
你可以在 throws 列出方法實際上不會拋出的異常:
void f() throws IOException { // 實際上不拋 IOException
// ...
}
編譯器會信任宣告,強制呼叫端處理 IOException。這在介面設計時可作為預留擴充點(佔位符),未來實作加入 I/O 操作時無需修改介面簽名。但濫用會誤導呼叫端,建議謹慎使用並附註 JavaDoc 說明。
throws,實作類可省略或縮減(拋出較少或較具體的異常),但不可擴大(新增父介面未宣告的已檢查異常)| 原則 | 說明 |
|---|---|
| 精確宣告 | 僅列出真正可能拋出的已檢查異常,避免「throws Exception」這種反模式 |
| 文件化原因 | 在 JavaDoc @throws 標籤說明何種情況拋出、呼叫端應如何處理 |
| 避免過度捕獲 | 若無法妥善處理,直接宣告向上拋出,交由更上層統一處理 |
| 區分已/未檢查 | 業務邏輯可預期失敗用已檢查異常;程式 Bug 用 RuntimeException |
throws 子句是 Java 靜態類型系統延伸到錯誤處理的關鍵機制。它強制開發者在設計階段就思考異常流向,雖增加些許撰寫負擔,卻大幅降低生產環境因遺漏處理而導致的非預期崩潰。善用編譯器這道防線,是寫出健壯 Java 程式的基石。