异常体系概述
Java 中的所有异常类型都是Throwable类的子类。异常体系分为两大主要分支:
- Error:表示程序无法处理的严重问题
- Exception:表示程序可以处理的异常情况
Exception 又进一步分为:
- Checked Exception(受检异常):编译器强制要求处理
- RuntimeException(运行时异常):编译器不强制处理
Throwable 根类
java.lang.Throwable是所有异常和错误的根类,定义了以下核心方法:
Error(严重错误)
Error及其子类代表应用程序无法处理的严重问题,通常由 JVM 或底层系统引起。
主要 Error 类型
- VirtualMachineError
虚拟机错误,JVM 内部错误
子类:
- OutOfMemoryError
// 当JVM无法分配足够内存时抛出
throw new OutOfMemoryError("Java heap space");
- StackOverflowError
// 当方法调用栈深度超过JVM限制时抛出(如无限递归)
public void infiniteRecursion() {
infiniteRecursion(); // 会抛出StackOverflowError
}
- InternalError
// JVM内部错误
throw new InternalError("Internal VM error");
- LinkageError
链接错误,类依赖关系问题
子类:
- NoClassDefFoundError
// 编译时存在类,但运行时无法找到.class文件
Class.forName("com.example.MissingClass"); // 可能抛出
- NoSuchMethodError
// 调用不存在的方法
object.missingMethod(); // 运行时抛出
- NoSuchFieldError
// 访问不存在的字段
object.missingField; // 运行时抛出
- UnsatisfiedLinkError
// 找不到native方法的本机语言定义
System.loadLibrary("missingLibrary"); // 可能抛出
- ClassFormatError
类格式错误
子类:
- UnsupportedClassVersionError
// 类文件版本不被当前JVM支持
// 例如用Java 17编译的类在Java 8上运行
- ThreadDeath
线程结束错误
// 当调用Thread.stop()时抛出
Thread.currentThread().stop();
Exception(异常)
Exception及其子类代表程序可以处理的异常情况。
主要 Exception 类型
- IOException
输入输出异常,处理文件、流操作时的错误
子类:
- FileNotFoundException
// 文件未找到
new FileInputStream("missingfile.txt"); // 必须处理
- EOFException
// 文件提前结束
inputStream.read(); // 可能抛出
- SocketException
// 套接字操作异常
socket.connect(new InetSocketAddress("host", port)); // 可能抛
- MalformedURLException
// URL格式错误
new URL("invalid-url"); // 必须处理
- SQLException
数据库访问异常
// 数据库操作错误
Connection conn = DriverManager.getConnection(url); // 必须处理
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql); // 可能抛出
- ClassNotFoundException
类未找到异常
// 动态加载类时无法找到
Class.forName("com.example.MissingClass"); // 必须处理
- InterruptedException
线程中断异常
// 线程在休眠或等待时被中断
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
}
- CloneNotSupportedException
克隆不支持异常
// 调用clone()但类未实现Cloneable接口
object.clone(); // 必须处理
- IllegalAccessException
非法访问异常
// 访问不允许访问的类或成员
field.setAccessible(true);
field.get(object); // 可能抛出
- InstantiationException
实例化异常
// 无法实例化类(如抽象类、接口)
Class.forName("com.example.AbstractClass").newInstance(); // 必须处理
Checked Exception(受检异常)
编译器强制要求处理的异常,必须使用try-catch捕获或throws声明。
完整 Checked Exception 列表
- java.io 包
- IOException
- FileNotFoundException
- EOFException
- SocketException
- MalformedURLException
- UnsupportedEncodingException
- FileAlreadyExistsException
- java.sql 包
- SQLException
- SQLTimeoutException
- BatchUpdateException
- SocketException
- BindException
- ConnectException
- UnknownHostException
- java.lang 包
- ClassNotFoundException
- CloneNotSupportedException
- IllegalAccessException
- InstantiationException
- InterruptedException
- NoSuchFieldException
- NoSuchMethodException
- java.util 包
- ParseException
- IllegalFormatException
- MissingResourceException
- java.security 包
- GeneralSecurityException
- NoSuchAlgorithmException
- InvalidKeyException
RuntimeException(运行时异常)
编译器不强制处理的异常,通常由程序逻辑错误引起。
主要 RuntimeException 类型
- NullPointerException
空指针异常
// 调用null对象的方法或访问字段
String s = null;
s.length(); // 抛出NullPointerException
- ArrayIndexOutOfBoundsException
数组下标越界异常
// 访问数组时使用无效索引
int[] arr = new int[5];
arr[10] = 1; // 抛出ArrayIndexOutOfBoundsException
- ClassCastException
类型转换异常
// 强制类型转换失败
Object obj = "string";
Integer num = (Integer) obj; // 抛出ClassCastException
- IllegalArgumentException
非法参数异常
// 方法接收到不合法的参数
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
- ArithmeticException
算术异常
// 算术运算错误
int result = 10 / 0; // 抛出ArithmeticException
- NumberFormatException
数字格式转换异常
// 字符串转换为数字失败
Integer.parseInt("abc"); // 抛出NumberFormatException
- IndexOutOfBoundsException
索引越界异常
// 访问集合时使用无效索引
List<String> list = new ArrayList<>();
list.get(0); // 抛出IndexOutOfBoundsException
- UnsupportedOperationException
不支持的操作异常
// 调用不支持的方法
List<String> unmodifiableList = Collections.unmodifiableList(new ArrayList<>());
unmodifiableList.add("element"); // 抛出UnsupportedOperationException
完整 RuntimeException 列表
- java.lang 包
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- IllegalArgumentException
- ArithmeticException
- NumberFormatException
- IndexOutOfBoundsException
- UnsupportedOperationException
- IllegalStateException
- NoSuchElementException
- ConcurrentModificationException
- java.util 包
- IllegalFormatConversionException
- InputMismatchException
- NoSuchElementException
- ConcurrentModificationException
- java.util.regex 包
- PatternSyntaxException
- IllegalArgumentException(在某些情况下)
异常处理最佳实践
- 异常处理原则
应该做的:
- 及时处理异常:对 Checked Exception 进行适当处理
- 提供有意义的信息:异常信息应该清晰、具体
- 记录异常日志:便于问题诊断和调试
- 在合适的层级处理:在能够有效处理的层级捕获异常
- 清理资源:使用 try-with-resources 确保资源释放
不应该做的:
- 忽略异常:避免空的 catch 块
- 捕获所有异常:避免使用catch (Exception e)
- 在 finally 块中返回值:可能覆盖异常信息
- 过度使用异常:不要用异常控制正常流程
- 不记录异常信息:难以诊断问题
- 异常处理模式
Try-With-Resources
// 自动关闭资源
try (FileInputStream fis = new FileInputStream("file.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
logger.error("Error reading file", e);
throw new CustomException("Failed to read file", e);
}
多层异常处理
public void businessOperation() throws BusinessException {
try {
dataAccessOperation();
} catch (SQLException e) {
logger.error("Database operation failed", e);
throw new BusinessException("Data access error", e);
}
}
private void dataAccessOperation() throws SQLException {
// 数据库操作
}
自定义异常
//自定义业务异常
public class BusinessException extends Exception {
private final String errorCode;
public BusinessException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
public BusinessException(String message, String errorCode, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
}
- 异常链处理
try {
// 操作1
operation1();
} catch (Exception e) {
// 记录异常
logger.error("Operation 1 failed", e);
// 包装为新异常并重新抛出
throw new BusinessException("Operation failed", e);
}
异常分类对比表
|-------|--------------------|------------------------|-------------------------------------------|
| 特性 | Error | Checked Exception | RuntimeException |
| 继承关系 | Throwable → Error | Throwable → Exception | Throwable → Exception → RuntimeException |
| 处理要求 | 否(编译器不要求) | 是(必须处理) | 否(编译器不要求) |
| 可恢复性 | 严重,通常不可恢复 | 可恢复 | 通常不可恢复 |
| 常见来源 | JVM 或系统环境 | 外部因素(文件、网络等) | 程序逻辑错误 |
| 处理方式 | 调整系统 / JVM 参数 | try-catch 或 throws | 修复代码逻辑 |
| 示例 | OutOfMemoryError | IOException | NullPointerException |
| 发生时机 | 运行时 | 编译时检查,运行时发生 | 运行时 |
异常处理决策表
|--------------------|------------|------------|
| 异常类型 | 建议处理方式 | 示例场景 |
| Error | 监控和报警 | 内存溢出、栈溢出 |
| Checked Exception | 立即处理或向上传递 | 文件操作、网络连接 |
| RuntimeException | 修复代码逻辑 | 空指针、数组越界 |
总结
Java 异常体系是确保程序健壮性的重要机制:
- Error:表示严重的系统级错误,程序通常无法恢复
- Checked Exception:表示可预见的外部问题,必须显式处理
- RuntimeException:表示程序逻辑错误,应该通过代码修复避免
最佳实践建议:
- 合理使用异常处理,不要过度使用
- 提供清晰的异常信息和完整的堆栈跟踪
- 优先修复 RuntimeException 的根本原因
- 对 Checked Exception 提供优雅的恢复机制
- 使用自定义异常封装业务逻辑错误
- 利用 try-with-resources 确保资源安全释放
通过理解和正确使用 Java 异常体系,可以编写出更加健壮、可维护的 Java 应用程序。