01. 异常的分类体系
Java 异常分两大类:受检异常(Checked Exception)和非受检异常(Unchecked Exception)。受检异常必须显式处理,要么 try-catch 要么 throws 声明,比如 IOException。非受检异常是 RuntimeException 的子类,不用强制处理,比如 NullPointerException。Error 是更严重的错误,程序一般无法处理,比如 OutOfMemoryError。
java
// 受检异常 - 必须处理
try {
FileReader reader = new FileReader("file.txt");
} catch (FileNotFoundException e) {
System.out.println("文件没找到");
}
// 非受检异常 - 不用强制处理
String s = null;
s.length(); // 抛出 NullPointerException02. try-catch-finally 机制
try 块放可能出错的代码,catch 捕获指定异常类型,finally 不论是否异常都会执行。多个 catch 按顺序匹配,子类异常写前面父类写后面。finally 典型用途是关闭资源(文件流、数据库连接),但 JDK7 之后 try-with-resources 更优雅。
java
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try { reader.close(); } catch (IOException e) { }
}
}不要在 finally 里写 return,会覆盖 try 块里的返回值。
03. try-with-resources 自动关闭
JDK7 引入的 try-with-resources 让资源管理变得超简单。把实现了 AutoCloseable 接口的资源声明在 try 后面的括号里,用完自动关闭,连 finally 都不用写。文件读写、数据库连接、网络连接这些都应该用这种写法。
java
public String readFile(String path) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}所有 IO 操作都用 try-with-resources,省心又安全。
04. 自定义异常
有些业务场景需要自己定义异常,比如用户不存在、余额不足。写一个类继承 Exception(受检)或 RuntimeException(非受检),加个构造函数就行了。业务代码里 throw new 抛出,上层 catch 统一处理。自定义异常让错误信息更有语义。
java
public class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String message) {
super(message);
}
}
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientBalanceException("余额不足: " + balance);
}
balance -= amount;
}05. 异常处理的最佳实践
不要 catch 了什么都不做(吞异常),至少打个日志。不要 catch Exception 这个最顶层的,太宽泛了会误吞异常。不要用异常控制正常业务流程,异常开销很大。抛出异常时要保留原始堆栈信息,用 new RuntimeException(message, e) 而不是丢掉了 cause。
java
// 错误:吞异常
catch (IOException e) { }
// 错误:用异常控制流程
if (x == 0) throw new RuntimeException("除数不能为零");
// 正确:保留原始异常
catch (SQLException e) {
throw new RuntimeException("数据库查询失败", e);
}
// 正确:打日志
catch (IOException e) {
log.error("读取文件失败", e);
throw new ServiceException("文件读取错误", e);
}永远不要用异常控制正常业务逻辑,异常的性能代价是正常流程的上百倍。
知识测验
第 1/4 题正确 0
NullPointerException 属于哪类异常?
下一节
下一节 IO 流