首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 異常處理核心:throw vs throws 區別、自定義異常、最佳實踐全解析

     編輯:Java 綜合教學

throw 與 throws 徹底講透:異常拋出、聲明、捕獲、自定義與工程化最佳實踐

異常處理是 Java 健壯性基石。本文通過對比示例、字節碼視角、實戰場景,徹底釐清 throw、throws、try-catch-finally 的異同與協作關係。

1. 核心區別速查表

2. 三種拋出異常方式

  1. JVM 自動拋出:NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException 等非受檢異常。
  2. throw 手動拋出:業務邏輯檢測到非法狀態時主動拋出。
    if (amount <= 0) throw new IllegalArgumentException("金額必須大於 0");
  3. 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. 捕獲異常黃金法則

  1. 具體優先:catch (SQLException e) 在 catch (Exception e) 之前。
  2. 異常不吞沒:catch 塊至少 log.error 或 throw new BizException(..., e) 保留堆疊。
  3. 資源在 finally 關閉:JDBC Connection、InputStream、Lock 必須在 finally 或 try-with-resources 釋放。
  4. 受檢異常轉非受檢:Service 層統一包裝為 RuntimeException 子類,Controller 層統一處理,避免 throws 污染業務簽名。
  5. 避免用異常控制流程:如 try { map.get(key).doSomething(); } catch (NPE) { ... } 應改為 if (map.containsKey(key))。

6. Java 7+ 語法糖

  • 多重捕獲:catch (IOException | SQLException e)。
  • try-with-resources:實現 AutoCloseable 自動關閉,無需 finally。
    try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) {
        // ...
    }
  • 精確重拋:catch (final Exception e) { throw e; } 保持異常類型不被擦除為 Exception。

7. 總結

  • throw = 動作(拋球),throws = 聲明(掛牌「可能拋球」)。
  • 受檢異常強制 throws/catch,非受檢異常靠約定自覺處理。
  • 工程化分層:DAO 拋受檢 → Service 包裝非受檢 → Controller 統一處理 → 返回標準錯誤碼。
  • 自定義異常攜帶 code、userMsg、cause,構建可追蹤、可國際化、可監控的異常體系。

掌握異常流向與分層策略,是寫出可維護、可觀測、高健壯性 Java 系統的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度throwthrows
位置方法體內語句方法簽名聲明
作用主動拋出異常對象實例聲明方法可能拋出的異常類型
語法throw new Exception();void foo() throws IOException
檢查時機運行時編譯時(受檢異常強制處理)
異常類型任意 Throwable 子類實例異常類(可多個,逗號分隔)
傳遞責任拋出即轉移控制權聲明由調用者負責捕獲/繼續拋出