Java 异常分类详解
一、异常体系概述
Java 中的所有可抛出对象都继承自 Throwable 类,它分为两大分支:
- Error(错误):系统级错误,通常由 JVM 生成,表示不可恢复的严重问题
- Exception(异常):程序级的异常,表示程序本身可以处理的问题
异常继承结构
Throwable
├── Error(系统错误,程序不需处理)
│ ├── VirtualMachineError(虚拟机错误)
│ │ ├── StackOverflowError(栈溢出)
│ │ └── OutOfMemoryError(内存溢出)
│ └── NoClassDefFoundError(类未找到)
│
└── Exception(程序异常)
├── RuntimeException(Unchecked Exception)(非受检异常,可不处理)
│ ├── NullPointerException(空指针)
│ ├── ArrayIndexOutOfBoundsException(数组越界)
│ ├── ArithmeticException(算术异常/除零)
│ ├── ClassCastException(类型转换)
│ └── IllegalArgumentException(非法参数)
│
└── Checked Exception(受检异常,必须处理)
├── IOException(输入输出)
├── SQLException(数据库)
├── ClassNotFoundException(类未找到)
└── FileNotFoundException(文件未找到)
二、Error(错误)
Error 表示 JVM 运行时的系统错误,通常由外部因素引起,程序无法主动捕获和处理。
| 错误类型 | 说明 | 常见场景 |
|---|---|---|
VirtualMachineError |
虚拟机错误 | JVM 本身出现问题 |
StackOverflowError |
栈溢出错误 | 递归调用没有正确终止 |
OutOfMemoryError |
内存溢出错误 | 对象创建过多,堆内存耗尽 |
NoClassDefFoundError |
类定义未找到错误 | 编译时类存在,运行时缺失 |
OutOfMemoryError: Metaspace |
元空间溢出 | 加载的类过多 |
示例代码
public class StackOverflowDemo {
// 无限递归会导致 StackOverflowError
public static void recursive() {
recursive(); // 没有终止条件
}
public static void main(String[] args) {
try {
recursive();
} catch (StackOverflowError e) {
System.out.println("栈溢出错误发生!");
}
}
}
三、Exception(异常)
Exception 是程序运行过程中可以捕获和处理的异常,分为两大类:
3.1 运行时异常(RuntimeException)
也称为非受检异常(Unchecked Exception),由程序逻辑错误引起,编译器不强制要求捕获或声明。
| 异常类型 | 说明 | 示例 |
|---|---|---|
NullPointerException |
空指针异常 | 调用 null 对象的方法 |
ArrayIndexOutOfBoundsException |
数组越界异常 | 访问不存在的索引 |
StringIndexOutOfBoundsException |
字符串越界异常 |
charAt() 传入非法索引 |
ArithmeticException |
算术异常 | 整数除以零 |
ClassCastException |
类型转换异常 | 强制转换不兼容的类型 |
IllegalArgumentException |
非法参数异常 | 方法参数不合法 |
NumberFormatException |
数字格式异常 | Integer.parseInt("abc") |
示例代码
public class RuntimeExceptionDemo {
public static void main(String[] args) {
// 1. 空指针异常
String str = null;
try {
System.out.println(str.length());
} catch (NullPointerException e) {
System.out.println("空指针异常:" + e.getMessage());
}
// 2. 数组越界
int[] arr = {1, 2, 3};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界:" + e.getMessage());
}
// 3. 除零异常
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("算术异常:" + e.getMessage());
}
// 4. 类型转换异常
Object obj = "Hello";
try {
Integer num = (Integer) obj;
} catch (ClassCastException e) {
System.out.println("类型转换异常:" + e.getMessage());
}
}
}
3.2 受检异常(Checked Exception)
也称为编译时异常,编译器强制要求捕获或声明抛出,由外部因素引起。
| 异常类型 | 说明 | 处理方式 |
|---|---|---|
IOException |
输入输出异常 | 必须 try-catch 或 throws |
SQLException |
数据库异常 | 必须 try-catch 或 throws |
ClassNotFoundException |
类未找到异常 | 必须 try-catch 或 throws |
FileNotFoundException |
文件未找到异常 | 必须 try-catch 或 throws |
InterruptedException |
中断异常 | 必须 try-catch 或 throws |
NoSuchMethodException |
方法未找到异常 | 必须 try-catch 或 throws |
示例代码
import java.io.FileReader;
import java.io.IOException;
public class CheckedExceptionDemo {
// 方式一:捕获异常
public void readFile() {
try (FileReader reader = new FileReader("test.txt")) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
System.out.println("文件读取失败:" + e.getMessage());
}
}
// 方式二:声明抛出
public void readFileThrows() throws IOException {
FileReader reader = new FileReader("test.txt");
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
reader.close();
}
}
四、异常处理最佳实践
4.1 异常捕获顺序
先捕获具体异常,再捕获通用异常:
try {
// 业务代码
} catch (NullPointerException e) {
// 先处理空指针
} catch (IOException e) {
// 再处理IO异常
} catch (Exception e) {
// 最后捕获通用异常
}
4.2 常见原则
| 原则 | 说明 |
|---|---|
| 异常要具体 | 捕获具体异常类型,而非泛型 Exception
|
| 不要吞掉异常 | 至少要记录日志 |
| 及时释放资源 | 使用 try-with-resources
|
| 避免大范围捕获 | 只捕获需要处理的异常 |
| 异常不能代替流程控制 | 不要用异常来做业务判断 |
4.3 try-with-resources 示例
// 传统写法
FileReader reader = null;
try {
reader = new FileReader("test.txt");
// 读取文件
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// try-with-resources 写法(推荐)
try (FileReader reader = new FileReader("test.txt")) {
// 读取文件
} catch (IOException e) {
e.printStackTrace();
}
五、总结
Throwable
├── Error(系统错误,程序不需处理)
│
└── Exception(程序异常)
│
├── RuntimeException(Unchecked Exception)(非受检异常,可不处理)
│
└── Checked Exception(受检异常,必须处理)
总结对比表
| 类别 | 父类 | 是否受检 | 处理要求 | 典型场景 |
|---|---|---|---|---|
| Error | Throwable | - | 不需要处理 | 系统级错误(栈溢出、内存溢出) |
| RuntimeException | Exception | 非受检 | 可不处理 | 程序逻辑错误(空指针、越界) |
| Checked Exception | Exception | 受检 | 必须捕获或声明 | 外部资源问题(IO、SQL) |
| 类别 | 是否受检 | 处理要求 | 典型场景 |
|---|---|---|---|
| Error | - | 不需要处理 | 系统级错误 |
| RuntimeException | 非受检 | 可不处理 | 程序逻辑错误 |
| Checked Exception | 受检 | 必须捕获或声明 | 外部资源问题 |