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

Java 異常處理核心:throw 與 throws 區別、異常鏈與最佳實踐

     編輯:Java 相關知識

核心區別一覽

維度throwthrows
位置方法體內部方法簽名聲明處
作用拋出具體異常對象實例聲明方法可能拋出的異常類型
語法throw new ExceptionType();throws ExceptionType1, ExceptionType2
執行時機運行時動態拋出編譯時靜態檢查
必須處理受檢異常需捕獲或繼續拋出調用者必須捕獲或繼續聲明

throw 詳細用法

public void validateAge(int age) {
    // 1. 拋出運行時異常 (無需聲明)
    if (age < 0) {
        throw new IllegalArgumentException("年齡不能為負數: " + age);
    }
    
    // 2. 拋出受檢異常 (方法必須聲明 throws)
    if (age > 150) {
        throw new InvalidAgeException("年齡超出合理範圍: " + age);
    }
    
    // 3. 捕獲後重新拋出 (保留堆棧)
    try {
        riskyOperation();
    } catch (IOException e) {
        throw new BusinessException("操作失敗", e); // 包裝異常鏈
    }
    
    // 4. 直接重拋捕獲的異常
    try {
        anotherRisky();
    } catch (SQLException e) {
        throw e; // 原樣拋出,堆棧保持不變
    }
}

throws 詳細用法

// 1. 聲明單一受檢異常
public void readFile(String path) throws IOException {
    Files.readAllLines(Paths.get(path));
}

// 2. 聲明多個異常
public void processData() throws IOException, SQLException, TimeoutException {
    // ...
}

// 3. 父類異常覆蓋子類 (不推薦,語義不清)
public void vague() throws Exception { // 太寬泛
    // ...
}

// 4. 覆寫方法的異常聲明規則
// 子類拋出的異常必須是父類聲明異常的子類或相同
class Parent {
    void method() throws IOException {}
}
class Child extends Parent {
    @Override
    void method() throws FileNotFoundException {} // ✓ 子類異常
    // void method() throws Exception {} // ✗ 父類未聲明
}

throw 與 throws 協作流程

// 服務層
public class UserService {
    public void register(User user) throws ValidationException, DuplicateUserException {
        // 業務校驗 - 主動拋出
        if (user.getName() == null) {
            throw new ValidationException("用戶名不能為空");
        }
        
        // 數據庫操作 - 可能拋出 SQLException
        try {
            userDao.insert(user);
        } catch (SQLException e) {
            if (e.getErrorCode() == 1062) { // MySQL 唯一鍵衝突
                throw new DuplicateUserException("用戶已存在", e); // 包裝拋出
            }
            throw new SystemException("數據庫錯誤", e); // 其他包裝
        }
    }
}

// 控制器層 - 捕獲處理
@RestController
public class UserController {
    @PostMapping("/register")
    public Result register(@RequestBody User user) {
        try {
            userService.register(user);
            return Result.ok();
        } catch (ValidationException e) {
            return Result.badRequest(e.getMessage());
        } catch (DuplicateUserException e) {
            return Result.conflict(e.getMessage());
        } catch (SystemException e) {
            log.error("註冊系統異常", e);
            return Result.error("系統繁忙");
        }
    }
}

三種異常拋出方式對比

方式代碼示例特點適用場景
JVM 自動拋出int a = 1/0; // ArithmeticException運行時錯誤、Bug編程錯誤、不可預期
throw 手動拋出throw new BizException("業務邏輯錯誤");業務規則違反、參數校驗失敗可預期的業務異常
throws 聲明拋出void method() throws IOException {}方法無法處理、需調用者決策受檢異常傳播、API 契約

異常鏈與原因保留 (關鍵最佳實踐)

// ✅ 正確:構造器傳入 cause,保留完整堆棧鏈
throw new BusinessException("訂單創建失敗", originalException);

// ❌ 錯誤:僅設置消息,丟失原始堆棧
throw new BusinessException("訂單創建失敗: " + originalException.getMessage());

// ✅ 正確:捕獲後包裝重拋
try {
    jdbcTemplate.update(sql);
} catch (DataAccessException e) {
    throw new PersistenceException("數據庫操作失敗", e);
}

// 異常鏈遍歷
Throwable cause = e;
while (cause != null) {
    System.out.println(cause.getClass().getName() + ": " + cause.getMessage());
    cause = cause.getCause();
}

編程規範建議

  1. 業務異常繼承 RuntimeException:非受檢,不強制捕獲,由全局異常處理器統一處理
  2. 受檢異常僅用於「調用者可恢復」場景:IO、網絡、數據庫等資源操作
  3. 方法簽名聲明具體異常:避免 throws Exception,語義不明
  4. 捕獲異常必須處理:記錄日誌、轉換拋出、恢復邏輯,嚴禁空 catch
  5. 異常包裝保留 cause:構造器傳入原始異常,保証診斷能力
  6. finally 不控制流程:不 return/throw,只做清理
  7. 自定義異常實現 Serializable:支持分布式傳輸

異常層級架構設計

Exception (受檢)
├── SystemException          // 系統級:數據庫、網絡、配置
│   ├── PersistenceException
│   ├── RemoteServiceException
│   └── ConfigurationException
└── ValidationException      // 參數校驗 (可受檢也可非受檢)

RuntimeException (非受檢)
├── BusinessException        // 業務邏輯:庫存不足、狀態錯誤
│   ├── InsufficientStockException
│   ├── InvalidStateException
│   └── DuplicateResourceException
├── SecurityException        // 認證授權
└── IllegalArgumentException // 參數非法 (JDK 內置)

清晰區分 throw (動作) 與 throws (聲明),構建分層異常體系,保留完整因果鏈,是構建可維護、可診斷 Java 系統的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved