核心區別一目了然
| 維度 | throw | throws |
| 作用位置 | 方法體內部,具體代碼行 | 方法聲明處,簽名部分 |
| 語義意圖 | 主動拋出具體異常對象 | 聲明方法可能拋出的異常類型 |
| 操作對象 | 異常實例(new Exception()) | 異常類(Exception.class) |
| 異常流向 | 立即中斷當前流程,向上傳遞 | 告知調用者需處理該異常 |
throw 實戰:主動拋出異常對象
拋出系統異常
public class ThrowDemo {
public void validateNumber(String str) {
// 手動構建異常對象並拋出
NumberFormatException e = new NumberFormatException("非法數字格式:" + str);
throw e; // 當前方法終止,異常向上傳遞
}
public static void main(String[] args) {
ThrowDemo demo = new ThrowDemo();
try {
demo.validateNumber("abc");
} catch (NumberFormatException e) {
System.out.println("捕獲異常:" + e.getMessage());
}
}
}
拋出自定義異常
// 自定義異常類
class AgeException extends Exception {
public AgeException(String msg) { super(msg); }
}
public class CustomThrow {
public static int checkAge(String ageStr) throws AgeException {
int age = Integer.parseInt(ageStr);
if (age < 0) {
// 條件檢查失敗,主動拋出業務異常
throw new AgeException("年齡不能為負數:" + age);
}
return age;
}
}
throws 實戰:方法簽名聲明異常
聲明拋出系統異常
public class ThrowsDemo {
// 聲明可能拋出 NumberFormatException
public void parseNumber(String str) throws NumberFormatException {
int num = Integer.parseInt(str); // 內部調用拋出異常的方法
// 無需 try-catch,異常自動向上傳遞
}
}
聲明拋出自定義異常(強制要求)
public class Service {
// 自定義檢查型異常必須聲明,否則編譯報錯
public void process(String input) throws AgeException, IOException {
if (input == null) throw new AgeException("輸入為空");
Files.readAllLines(Paths.get(input)); // 拋出 IOException
}
}
實戰決策指南:何時用哪個?
系統異常(RuntimeException 及子類)
- 可選用
throws 聲明(文檔化意圖,便於團隊協作)
- 或內部
try-catch 消化(框架層常見)
- 即使不聲明,JVM 也會自動拋出
檢查型異常(繼承 Exception 非 RuntimeException)
- 必須 在方法簽名用
throws 聲明
- 或內部
try-catch 完整處理
- 否則編譯器強制報錯
自定義業務異常
- 繼承
Exception → 檢查型 → 強制 throws 聲明
- 繼承
RuntimeException → 非檢查型 → 可選聲明
- 建議:業務邏輯異常用檢查型,編程錯誤用非檢查型
最佳實踐總結
throw 用於具體業務邏輯點主動觸發異常
throws 用於方法契約層面聲明風險
- 自定義異常攜帶豐富上下文信息(錯誤碼、參數快照)
- 異常鏈保持完整:
throw new BizException("原因", originalException)
- 避免異常吞沒:至少記錄日誌再轉換拋出