异常体系结构
所有异常和错误的根类是 java.lang.Throwable,它有两个主要子类:
Error:表示严重错误,通常是 JVM 内部错误(如OutOfMemoryError、StackOverflowError)。应用程序不应捕获或处理,因为程序已经无法恢复。Exception:表示程序可以处理的异常情况,分为两大类:- 受检异常(Checked Exception):编译器强制要求处理(捕获或声明抛出)。如
IOException、SQLException、ClassNotFoundException。 - 非受检异常(Unchecked Exception):编译器不强制处理,通常是程序逻辑错误导致。包括
RuntimeException及其子类(如NullPointerException、IllegalArgumentException、ArithmeticException)。
text
Throwable
├── Error (不可恢复,不处理)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception
├── IOException (受检)
├── SQLException (受检)
├── ClassNotFoundException (受检)
└── RuntimeException (非受检)
├── NullPointerException
├── IndexOutOfBoundsException
├── ArithmeticException
└── ...
异常处理关键字
| 关键字 | 作用 |
|---|---|
try | 包裹可能抛出异常的代码块 |
catch | 捕获并处理特定类型的异常 |
finally | 无论是否发生异常都会执行的代码块(用于释放资源) |
throw | 手动抛出异常对象(用于方法内部) |
throws | 声明方法可能抛出的异常类型(用于方法签名) |
try {
// 可能抛出异常的代码
int result = 10 / 0;
} catch (ArithmeticException e) {
// 捕获特定异常并处理
System.out.println("除数不能为零:" + e.getMessage());
} catch (Exception e) {
// 捕获更通用的异常(应放在具体异常之后)
e.printStackTrace();
} finally {
// 无论是否异常都会执行,常用于关闭文件、数据库连接等
System.out.println("资源释放");
}
- 多个
catch块:按顺序匹配,应将子类异常放在前,父类放在后,否则子类永远捕获不到。 finally块:即使try或catch中有return,finally仍会执行(除非System.exit(0)终止 JVM)。
throw 与 throws
throw:在方法内部手动抛出异常对象。
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
throws:在方法声明时告知调用者该方法可能抛出的受检异常。
public void readFile(String path) throws IOException {
// ...
}
受检异常 vs 非受检异常
| 类型 | 特点 | 处理要求 | 典型例子 |
|---|---|---|---|
| 受检异常 | 编译器会检查 | 必须 try-catch 或在方法上 throws | IOException, SQLException |
| 非受检异常 | 编译器不强制 | 可处理也可不处理(但建议处理逻辑错误) | NullPointerException, ArithmeticException |
设计原则:
- 受检异常用于可预见的、调用方有能力恢复的情况(如文件不存在,可提示用户换文件)。
- 非受检异常用于编程错误(如空指针、数组越界),应由开发者修正代码,而不是捕获。
自定义异常
当 JDK 内置异常无法准确描述业务问题时,可以创建自己的异常类。
自定义受检异常(继承 Exception)
public class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
自定义非受检异常(继承 RuntimeException)
public class InvalidUserInputException extends RuntimeException {
public InvalidUserInputException(String message) {
super(message);
}
}
使用示例
public class BankAccount {
private double balance;
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("余额不足,需要 " + amount + ",现有 " + balance);
}
balance -= amount;
}
}
异常处理中的常见坑
finally块中return或throw会覆盖try/catch中的返回值,应避免。 java
try {
return 1;
} finally {
return 2; // 最终返回 2
}