Java 常見例外
了解常見的例外類型可以幫助你更好地處理和預防程式錯誤。
例外分類
Throwable
├── Error(系統錯誤,不應捕捉)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception
├── RuntimeException(Unchecked Exception)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ...
└── 其他 Exception(Checked Exception)
├── IOException
├── SQLException
└── ...
Unchecked Exception
不需要強制處理,通常是程式邏輯錯誤。
NullPointerException
存取 null 物件的成員:
String s = null;
int length = s.length(); // NullPointerException
// 預防方式
if (s != null) {
int length = s.length();
}
// 或使用 Optional
Optional.ofNullable(s).ifPresent(str -> {
System.out.println(str.length());
});
// Java 14+ 顯示更詳細的訊息
// Cannot invoke "String.length()" because "s" is null
ArrayIndexOutOfBoundsException
陣列索引超出範圍:
int[] arr = {1, 2, 3};
int value = arr[5]; // ArrayIndexOutOfBoundsException
// 預防方式
if (index >= 0 && index < arr.length) {
int value = arr[index];
}
StringIndexOutOfBoundsException
字串索引超出範圍:
String s = "Hello";
char c = s.charAt(10); // StringIndexOutOfBoundsException
// 預防方式
if (index >= 0 && index < s.length()) {
char c = s.charAt(index);
}
IndexOutOfBoundsException
集合索引超出範圍:
List<String> list = Arrays.asList("a", "b", "c");
String s = list.get(10); // IndexOutOfBoundsException
ClassCastException
無效的型別轉換:
Object obj = "Hello";
Integer num = (Integer) obj; // ClassCastException
// 預防方式
if (obj instanceof Integer) {
Integer num = (Integer) obj;
}
// Java 16+ pattern matching
if (obj instanceof Integer num) {
System.out.println(num);
}
IllegalArgumentException
非法參數:
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年齡必須在 0-150 之間");
}
this.age = age;
}
IllegalStateException
非法狀態:
public class Connection {
private boolean connected = false;
public void sendData(String data) {
if (!connected) {
throw new IllegalStateException("尚未連線");
}
// 發送資料
}
}
ArithmeticException
算術錯誤:
int result = 10 / 0; // ArithmeticException
// 預防方式
if (divisor != 0) {
int result = 10 / divisor;
}
NumberFormatException
數字格式錯誤:
int num = Integer.parseInt("abc"); // NumberFormatException
// 預防方式
public static Integer parseIntSafe(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return null;
}
}
UnsupportedOperationException
不支援的操作:
List<String> list = Arrays.asList("a", "b", "c");
list.add("d"); // UnsupportedOperationException(固定大小的 List)
// 解決方式
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.add("d"); // OK
ConcurrentModificationException
並發修改錯誤:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
list.remove(s); // ConcurrentModificationException
}
// 解決方式:使用 Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
it.remove(); // OK
}
// 或使用 removeIf
list.removeIf(s -> s.equals("a"));
Checked Exception
必須處理或宣告的例外。
IOException
I/O 操作錯誤:
try {
FileReader reader = new FileReader("file.txt");
} catch (IOException e) {
System.out.println("檔案讀取錯誤:" + e.getMessage());
}
FileNotFoundException
檔案不存在:
try {
FileInputStream fis = new FileInputStream("nonexistent.txt");
} catch (FileNotFoundException e) {
System.out.println("檔案不存在");
}
SQLException
資料庫操作錯誤:
try {
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
} catch (SQLException e) {
System.out.println("資料庫錯誤:" + e.getMessage());
}
ParseException
解析錯誤:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = sdf.parse("invalid-date");
} catch (ParseException e) {
System.out.println("日期格式錯誤");
}
InterruptedException
執行緒中斷:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新設定中斷狀態
System.out.println("執行緒被中斷");
}
ClassNotFoundException
類別找不到:
try {
Class.forName("com.example.NonExistentClass");
} catch (ClassNotFoundException e) {
System.out.println("類別不存在");
}
Error(系統錯誤)
通常不應該捕捉,表示嚴重的系統問題。
OutOfMemoryError
記憶體不足:
// 不建議捕捉,但可以這樣預防
try {
byte[] hugeArray = new byte[Integer.MAX_VALUE];
} catch (OutOfMemoryError e) {
System.out.println("記憶體不足");
// 清理資源
}
StackOverflowError
堆疊溢位(通常是無限遞迴):
public void infiniteRecursion() {
infiniteRecursion(); // StackOverflowError
}
例外處理最佳實踐
1. 精確捕捉
// ✗ 太籠統
try {
// ...
} catch (Exception e) {
// ...
}
// ✓ 精確捕捉
try {
// ...
} catch (FileNotFoundException e) {
// 處理檔案不存在
} catch (IOException e) {
// 處理其他 I/O 錯誤
}
2. 不要忽略例外
// ✗ 忽略例外
try {
// ...
} catch (Exception e) {
// 什麼都不做
}
// ✓ 至少記錄
try {
// ...
} catch (Exception e) {
logger.error("發生錯誤", e);
}
3. 提供有意義的訊息
throw new IllegalArgumentException("使用者 ID 必須大於 0,實際值:" + userId);
4. 在適當的層級處理
// 低層級:拋出技術性例外
public byte[] readFile(String path) throws IOException {
return Files.readAllBytes(Paths.get(path));
}
// 高層級:轉換為業務例外或處理
public Config loadConfig() {
try {
byte[] data = readFile("config.json");
return parseConfig(data);
} catch (IOException e) {
throw new ConfigurationException("無法載入設定檔", e);
}
}
常見例外速查表
| 例外 | 說明 | 類型 |
|---|---|---|
| NullPointerException | 存取 null 物件 | Unchecked |
| ArrayIndexOutOfBoundsException | 陣列索引越界 | Unchecked |
| ClassCastException | 型別轉換錯誤 | Unchecked |
| IllegalArgumentException | 非法參數 | Unchecked |
| IllegalStateException | 非法狀態 | Unchecked |
| ArithmeticException | 算術錯誤 | Unchecked |
| NumberFormatException | 數字格式錯誤 | Unchecked |
| IOException | I/O 錯誤 | Checked |
| FileNotFoundException | 檔案不存在 | Checked |
| SQLException | 資料庫錯誤 | Checked |
| ParseException | 解析錯誤 | Checked |
| InterruptedException | 執行緒中斷 | Checked |
重點整理
- Unchecked Exception:程式邏輯錯誤,不需要強制處理
- Checked Exception:外部因素導致,必須處理或宣告
- Error:系統級錯誤,通常不應捕捉
- 精確捕捉例外,不要使用過於籠統的 catch
- 不要忽略例外,至少要記錄
- 在適當的層級處理例外