| 維度 | throw | throws |
| 位置 | 方法體內語句 | 方法簽名聲明 |
| 作用 | 主動拋出異常對象實例 | 聲明方法可能拋出的異常類型 |
| 語法 | throw new Exception(); | void foo() throws IOException |
| 檢查時機 | 運行時 | 編譯時(受檢異常強制處理) |
| 異常類型 | 任意 Throwable 子類實例 | 異常類(可多個,逗號分隔) |
| 傳遞責任 | 拋出即轉移控制權 | 聲明由調用者負責捕獲/繼續拋出 |
2. 三種拋出異常方式
- JVM 自動拋出:
NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException 等非受檢異常。
- throw 手動拋出:業務邏輯檢測到非法狀態時主動拋出。
if (amount <= 0) throw new IllegalArgumentException("金額必須大於 0");
- throws 聲明拋出:受檢異常(
Exception 子類非 RuntimeException)必須 throws 或 try-catch 處理。
public void readConfig() throws IOException { // 受檢異常
Files.readAllLines(Paths.get("config.yaml"));
}
3. 協作模式:throw + throws + try-catch
// 服務層:捕獲底層異常 → 包裝業務異常 → throw
public void transfer(TransferDTO dto) throws BizException {
try {
accountDao.deduct(dto.getFrom(), dto.getAmount());
accountDao.add(dto.getTo(), dto.getAmount());
} catch (SQLException e) {
log.error("轉賬失敗", e);
throw new BizException("TRANSFER_FAILED", "系統繁忙,請稍後重試", e); // 保留原因鏈
}
}
// Controller 層:統一異常處理器捕獲 BizException 返回統一錯誤碼
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public R<Void> handleBiz(BizException e) {
return R.fail(e.getCode(), e.getMessage());
}
}
4. 自定義異常設計規範
// 基礎業務異常:攜帶錯誤碼、用戶提示、原始原因
public class BizException extends RuntimeException {
private final String code;
private final String userMsg;
public BizException(String code, String userMsg) { this(code, userMsg, null); }
public BizException(String code, String userMsg, Throwable cause) {
super(userMsg, cause);
this.code = code;
this.userMsg = userMsg;
}
// getter...
}
// 錯誤碼枚舉:統一管理,前後端對齊
public enum ErrorCode {
PARAM_INVALID("400", "參數校驗失敗"),
TRANSFER_FAILED("500100", "轉賬失敗"),
IDEMPOTENT_CONFLICT("409", "重複請求");
// ...
}
5. 捕獲異常黃金法則
- 具體優先:
catch (SQLException e) 在 catch (Exception e) 之前。
- 異常不吞沒:
catch 塊至少 log.error 或 throw new BizException(..., e) 保留堆疊。
- 資源在 finally 關閉:JDBC Connection、InputStream、Lock 必須在
finally 或 try-with-resources 釋放。
- 受檢異常轉非受檢:Service 層統一包裝為
RuntimeException 子類,Controller 層統一處理,避免 throws 污染業務簽名。
- 避免用異常控制流程:如
try { map.get(key).doSomething(); } catch (NPE) { ... } 應改為 if (map.containsKey(key))。
6. Java 7+ 語法糖
7. 總結
throw = 動作(拋球),throws = 聲明(掛牌「可能拋球」)。
- 受檢異常強制
throws/catch,非受檢異常靠約定自覺處理。
- 工程化分層:DAO 拋受檢 → Service 包裝非受檢 → Controller 統一處理 → 返回標準錯誤碼。
- 自定義異常攜帶
code、userMsg、cause,構建可追蹤、可國際化、可監控的異常體系。
掌握異常流向與分層策略,是寫出可維護、可觀測、高健壯性 Java 系統的關鍵。
- 上一篇文章: Java 繼承構造器陷阱:Implicit super constructor 錯誤原因與三種修復方案
- 下一篇文章: Spring MVC 表單提交後台接收不到數據?5 大常見原因與排查清單
Copyright ©
程式碼之家 All Rights Reserved