checked vs unchecked
Java 独有两类异常:
| 派生自 | 必须 catch 或 throws | 例子 | |
|---|---|---|---|
| checked | Exception(非 RuntimeException) |
✅ | IOException、SQLException |
| unchecked | RuntimeException |
❌ | NullPointerException、IllegalArgument、IndexOutOfBounds |
| Error | Error |
不该 catch | OutOfMemoryError、StackOverflowError |
抛异常
public void withdraw(double amount) {
if (amount <= 0)
throw new IllegalArgumentException("金额必须 > 0");
if (amount > balance)
throw new InsufficientFundsException(balance, amount);
balance -= amount;
}
IllegalArgumentException 是 unchecked——不需要 throws 声明。
受检异常的传播
public String read() throws IOException { // 必须声明
return Files.readString(path);
}
// 调用方处理
try {
var s = read();
} catch (IOException e) {
...
}
// 或继续向上传
public String load() throws IOException { // 转抛
return read();
}
try / catch / finally
try {
doRisky();
} catch (FileNotFoundException e) {
System.err.println("没找到: " + e.getMessage());
} catch (IOException e) { // 父异常
System.err.println("IO 错: " + e);
} catch (Exception e) {
System.err.println("其他: " + e);
} finally {
cleanup(); // 无论成功失败都执行
}
catch 顺序从具体到一般——子异常在前。
多类型 catch
try {
...
} catch (IOException | SQLException e) { // | 分隔
// e 类型是 IOException 和 SQLException 的共同父类
handle(e);
}
try-with-resources
try (var reader = Files.newBufferedReader(path);
var writer = Files.newBufferedWriter(out)) {
// 用 reader / writer
reader.lines().forEach(writer::println);
} // 自动 close(哪怕中间抛异常)
任何 AutoCloseable 都能放。取代 Java 6 时代的 finally { try { resource.close(); } ... }。
异常链
try {
doSomething();
} catch (SQLException e) {
throw new RepositoryException("查询失败", e); // 原因放第二个参数
}
调用方拿到 RepositoryException + getCause() 拿原 SQLException——堆栈链完整。
重新抛
try {
doSomething();
} catch (Exception e) {
log.error("失败", e);
throw e; // ✅ 保留原异常
// throw new Exception(e); // 包装
}
throw e; 直接重抛——堆栈不变。
自定义异常
public class InsufficientFundsException extends RuntimeException {
private final double available;
private final double requested;
public InsufficientFundsException(double available, double requested) {
super("余额 " + available + " 不足扣 " + requested);
this.available = available;
this.requested = requested;
}
public double getAvailable() { return available; }
public double getRequested() { return requested; }
}
约定:
- 继承
RuntimeException(首选——除非真的需要强制调用方处理) - 名字以
Exception结尾 - 提供
(String)/(String, Throwable)构造
checked 异常的争议
Java 独有,社区争议大:
支持:编译器强制处理 → 不会漏。 反对:
- 让 lambda / Stream / Optional 等函数式 API 别扭
- 强迫 throws 链一路声明
- 容易导致"catch 后吞掉"反模式
现代 Java 风格:业务异常用 RuntimeException 子类。受检异常只在真"必须处理"的边界(如 IO)。
Spring / Kotlin / 大多数现代 Java 框架都基本不抛受检异常。
不要吞异常
// ❌ 灾难
try {
doSomething();
} catch (Exception e) {
// 静默忽略
}
// ✅ 至少日志
try {
doSomething();
} catch (Exception e) {
log.error("失败", e);
throw new RuntimeException(e); // 或继续抛
}
最差的代码模式之一——出问题永远不知道原因。
try-finally vs try-with-resources
// 老
var f = new FileInputStream(path);
try {
process(f);
} finally {
f.close(); // 但 close 也可能抛异常 → 主异常被覆盖
}
// 新
try (var f = new FileInputStream(path)) {
process(f);
} // 自动 close + 抑制异常(getSuppressed 拿)
新代码全 try-with-resources。
catch 之后的判断
try {
fetch(url);
} catch (HttpException e) {
if (e.getStatusCode() == 404) {
// 处理 404
} else {
throw e; // 不处理就重抛
}
}
或用 Java 8+ 简化模式:
catch (HttpException e) when (...) { // ❌ Java 没有,C# 有
}
Java 没 catch when——只能 catch 完手动 if。
不该 catch 的异常
// 通常是 bug,让程序崩溃修
NullPointerException
IllegalArgumentException
IllegalStateException
IndexOutOfBoundsException
ClassCastException
ArrayStoreException
// 系统级,无法处理
OutOfMemoryError
StackOverflowError
ThreadDeath
catch 应该是恢复 / 兜底——bug 让它崩,崩了就修。
全局兜底
Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
log.error("未处理异常 in {}", thread, e);
});
最后一线日志。Spring Boot 等框架自动配。
性能
异常开销大——fillInStackTrace 抓堆栈是慢操作。不要用异常做控制流:
// ❌ 慢
try {
int n = Integer.parseInt(s);
use(n);
} catch (NumberFormatException e) {
use(0);
}
// ✅ 快——预检
if (s.chars().allMatch(Character::isDigit)) {
use(Integer.parseInt(s));
} else {
use(0);
}
// 或在 Java 里没 TryParse,但有 OptionalInt
实战清单
- 业务异常 → 继承 RuntimeException
- 资源管理 → try-with-resources
- 不要 catch 不能处理的异常(让它向上传)
- 永远不要吞异常(至少日志)
- 不要用异常做控制流(用 Optional / TryXxx 模式)
→ 下一篇 Java 路线图