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

Java 常見運行時異常 (RuntimeException) 體系、成因與處理最佳實踐

     編輯:Java 相關知識

前言

Java 異常機制是程式健壯性的基石。RuntimeException (非受檢異常) 及其子類佔據了開發中 80% 以上的異常場景。本文系統梳理異常體系架構、核心概念、常見 RuntimeException 成因與處理策略,助你寫出更可靠的代碼。

一、異常體系架構總覽

java.lang.Throwable
├── java.lang.Error                 // 嚴重錯誤 (JVM 層面,不可恢復)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── NoClassDefFoundError
└── java.lang.Exception             // 一般異常
    ├── java.lang.RuntimeException  // ★ 非受檢異常 (Unchecked)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── ClassCastException
    │   ├── IllegalArgumentException
    │   ├── NumberFormatException
    │   ├── ArithmeticException
    │   ├── IndexOutOfBoundsException
    │   ├── IllegalStateException
    │   ├── UnsupportedOperationException
    │   └── ... (邏輯/程式設計錯誤)
    └── 其他 Exception 子類         // 受檢異常 (Checked)
        ├── IOException
        ├── SQLException
        ├── ClassNotFoundException
        ├── InterruptedException
        └── ... (環境/外部資源錯誤)

二、核心概念釐清

概念說明代表類別處理策略
ErrorJVM 層面嚴重問題,應用無法恢復OOM, StackOverflow僅能記錄日誌、優雅退出,不可 try-catch 恢復
RuntimeException**程式設計邏輯錯誤**,可透過防禦性程式設計避免NPE, 索引越界, 類型轉換**修正代碼邏輯**,而非單純 catch 處理
Checked Exception外部環境不確定性 (IO、網路、資料庫),**必須**處理IOException, SQLExceptiontry-catch 處理、拋出、轉換為業務異常

關鍵區別:C++ vs Java 異常分類

  • C++: logic_error (邏輯錯誤) ≈ Java RuntimeException;runtime_error (執行時環境錯誤) ≈ Java Checked Exception。
  • Java 命名 RuntimeException 易誤導:所有異常皆在運行時拋出。實質區分在於 **「是否為程式設計錯誤」**。

三、高頻 RuntimeException 深度解析

1. NullPointerException (NPE) - 佔比最高

// 典型成因
String s = null;
s.length(); // 💥

List list = new ArrayList<>();
list.add(null);
list.get(0).toString(); // 💥 元素為 null

// 防禦策略
// ✅ 使用 Objects.requireNonNull (Java 7+)
public void setName(String name) {
    this.name = Objects.requireNonNull(name, "姓名不可為空");
}

// ✅ Optional 避免顯式 null 檢查 (Java 8+)
public String getNameOrDefault(User user) {
    return Optional.ofNullable(user)
                   .map(User::getName)
                   .orElse("匿名");
}

// ✅ 註解標記 @NonNull/@Nullable (IDE/靜態分析工具支援)

2. ArrayIndexOutOfBoundsException / IndexOutOfBoundsException

// 成因
int[] arr = new int[3];
arr[3] = 10; // 💥 索引 0~2

List list = Arrays.asList("a", "b");
list.get(2); // 💥

// 防禦策略
// ✅ 使用增強 for / Stream 避免手動索引
for (String s : list) { ... }
list.forEach(s -> ...);

// ✅ 必須用索引時,先檢查 bounds
if (index >= 0 && index < arr.length) { ... }

// ✅ 使用 List.getOrDefault (Java 9+) / 手動封裝

3. ClassCastException

// 成因
Object obj = "hello";
Integer num = (Integer) obj; // 💥 String 不能轉 Integer

List list = new ArrayList(); // 原始類型
list.add("string");
Integer i = (Integer) list.get(0); // 💥 執行時才發現

// 防禦策略
// ✅ 使用泛型 杜絕原始類型
List list = new ArrayList<>();
list.add("string");
// String s = list.get(0); // 編譯期保證類型安全

// ✅ instanceof 檢查後再轉型
if (obj instanceof Integer) {
    Integer num = (Integer) obj;
}

// ✅ 模式匹配 instanceof (Java 16+)
if (obj instanceof Integer num) { // 直接宣告變數
    num.intValue();
}

4. IllegalArgumentException / IllegalStateException

// 區別
// IllegalArgumentException: 方法參數不合法 (前置條件違反)
public void setAge(int age) {
    if (age < 0 || age > 150) 
        throw new IllegalArgumentException("年齡範圍 0-150: " + age);
    this.age = age;
}

// IllegalStateException: 物件狀態不合法 (方法調用時機錯誤)
public void startEngine() {
    if (this.status != Status.STOPPED)
        throw new IllegalStateException("引擎已啟動,不可重複啟動");
    this.status = Status.RUNNING;
}

5. NumberFormatException

// 成因
Integer.parseInt("123abc"); // 💥
Double.parseDouble("3.14.15"); // 💥

// 防禦策略
// ✅ 使用正則預驗證 / 嘗試解析封裝
public static Integer parseIntSafe(String s) {
    try {
        return Integer.parseInt(s.trim());
    } catch (NumberFormatException e) {
        return null; // 或拋出業務異常
    }
}

// ✅ Java 11+ NumberFormatException 訊息更詳細,可直接記錄

6. ArithmeticException

int a = 10 / 0; // 💥 整數除零
// 浮點數除零不拋異常,返回 Infinity/NaN
double d = 10.0 / 0.0; // Infinity

四、異常處理最佳實踐原則

1. 捕獲並處理「你知道如何處理」的異常,拋出「你不知道如何處理」的異常

// ✅ 正確:資料層捕獲 SQLException,轉換為業務異常拋出
public User findById(String id) {
    try {
        return jdbc.queryForObject(sql, rowMapper, id);
    } catch (EmptyResultDataAccessException e) {
        return null; // 正常業務情況:查無資料
    } catch (SQLException e) {
        throw new DataAccessException("查詢用戶失敗", e); // 包裝拋出
    }
}

// ❌ 錯誤:吞噬異常
try { ... } catch (Exception e) { 
    e.printStackTrace(); // 僅印堆疊,調用者不知失敗
    return null; 
}

2. finally / try-with-resources 確保資源釋放

// ✅ Java 7+ try-with-resources (自動關閉實現 AutoCloseable)
try (Connection conn = ds.getConnection();
     PreparedStatement ps = conn.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {
    // 使用資源
} // 自動關閉 rs, ps, conn (即使拋異常)

3. 異常鏈保留原始堆疊

// ✅ 建構子傳入 cause
throw new BusinessException("訂單建立失敗", originalException);

// ❌ 丟失堆疊
throw new BusinessException("訂單建立失敗"); // 原因丟失

4. 避免過度使用異常控制流程

// ❌ 反模式:用異常做流程控制
try {
    while (true) list.get(index++);
} catch (IndexOutOfBoundsException e) {
    // 結束迴圈
}

// ✅ 正確:用條件判斷
while (index < list.size()) { ... }

5. 自定義異常設計原則

  • 繼承 RuntimeException (業務邏輯錯誤) 或 Exception (受檢業務異常)。
  • **類別名稱即核心資訊** (如 InsufficientBalanceException),避免過度設計異常類別結構。
  • 提供 String message 建構子,可選 Throwable cause。

五、常見異常對應表 (速查)

異常典型成因預防關鍵字
NullPointerException呼叫 null 物件方法/欄位Objects.requireNonNull, Optional, @NonNull
ArrayIndexOutOfBoundsException陣列/List 索引超出範圍增強 for, Stream, 邊界檢查
ClassCastException強制轉型失敗泛型, instanceof, 模式匹配
IllegalArgumentException參數值不合法前置條件檢查, 驗證框架
NumberFormatException字串解析數字失敗正則驗證, 安全解析工具
IllegalStateException物件狀態不允許操作狀態機設計, 狀態檢查
ConcurrentModificationException迭代中修改集合Iterator.remove, CopyOnWriteList, Stream
UnsupportedOperationException不可修改集合調用 add/removeList.of, Collections.unmodifiableList

六、生產環境異常監控建議

  1. **統一異常處理**:Spring @ControllerAdvice + @ExceptionHandler 統一轉換為標準錯誤回應 (JSON)。
  2. **日誌記錄**:log.error("業務失敗", e) 保留完整堆疊,**避免** e.printStackTrace()。
  3. **告警分級**:NPE、OOM、資料庫連線失敗 → 立即告警;業務驗證失敗 → 記錄不告警。
  4. **追蹤 ID**:請求進入生成 traceId,貫穿日誌、異常、回應頭,便於鏈路追蹤。

結語

RuntimeException 本質是**程式設計缺陷**的體現。掌握體系架構、熟知高頻異常成因、奉行「防禦性程式設計」與「精準異常處理」原則,才能從根源減少線上故障,提升系統穩定性。**記住:最好的異常處理,是寫出不拋異常的代碼。**

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