1807 字
约 6 分钟
7
第43章_异常基础
2026-09-24
无标签
学习目标
- 理解Java异常的概念和作用
- 掌握Java异常的分类体系
- 学会区分检查异常、运行时异常和错误
- 掌握异常处理的基本语法
- 了解异常处理的最佳实践
- 学会编写健壮的异常处理代码
什么是异常?
异常(Exception)是程序运行过程中出现的非正常情况,它会中断程序的正常执行流程。Java提供了完善的异常处理机制来处理这些异常情况。
异常的作用:错误报告:提供详细的错误信息程序健壮性:防止程序因错误而崩溃错误恢复:允许程序从错误中恢复调试支持:提供堆栈跟踪信息
异常示例
// 空指针异常示例
String str = null;
int length = str.length(); // 抛出 NullPointerException
// 数组越界异常示例
int[] array = {1, 2, 3};
int value = array[5]; // 抛出 ArrayIndexOutOfBoundsException
// 算术异常示例
int result = 10 / 0; // 抛出 ArithmeticException
Java异常分类
Java异常采用层次结构设计,所有异常都继承自Throwable类。
Throwable
├── Error
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception
├── RuntimeException (运行时异常)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── NumberFormatException
└── 检查异常
├── IOException
├── ClassNotFoundException
└── SQLException
1. 检查异常(Checked Exception)
检查异常是编译时必须处理的异常,继承自Exception但不是RuntimeException。
特点:
- 编译时必须处理
- 通常表示可恢复的错误
- 必须用try-catch捕获或throws声明
| 异常类型 | 描述 | 常见场景 |
|---|---|---|
| IOException | 输入输出异常 | 文件读写、网络通信 |
| ClassNotFoundException | 类未找到异常 | 动态加载类 |
| SQLException | 数据库异常 | 数据库操作 |
| InterruptedException | 线程中断异常 | 多线程编程 |
// 检查异常示例
try {
FileReader file = new FileReader("test.txt");
// 文件操作
} catch (FileNotFoundException e) {
System.out.println("文件未找到: " + e.getMessage());
}
2. 运行时异常(Runtime Exception)
运行时异常是编译时不强制处理的异常,继承自RuntimeException。
特点:编译时不强制处理通常表示编程错误可以选择捕获或让其传播
| 异常类型 | 描述 | 常见原因 |
|---|---|---|
| NullPointerException | 空指针异常 | 对null引用调用方法 |
| ArrayIndexOutOfBoundsException | 数组越界异常 | 访问超出数组范围的索引 |
| ArithmeticException | 算术异常 | 除零操作 |
| NumberFormatException | 数字格式异常 | 字符串转数字格式错误 |
| IllegalArgumentException | 非法参数异常 | 方法参数不合法 |
// 运行时异常示例
try {
String str = "abc";
int number = Integer.parseInt(str); // NumberFormatException
} catch (NumberFormatException e) {
System.out.println("数字格式错误: " + e.getMessage());
}
3. 错误(Error)
错误表示严重的系统问题,通常不应该被捕获。
特点:
- 表示严重的系统问题
- 通常不应该被捕获
- 程序无法恢复
| 错误类型 | 描述 | 常见原因 |
|---|---|---|
| OutOfMemoryError | 内存不足错误 | 堆内存耗尽 |
| StackOverflowError | 栈溢出错误 | 递归调用过深 |
| NoClassDefFoundError | 类定义未找到错误 | 运行时找不到类文件 |
异常处理语法
基本语法
try {
// 可能抛出异常的代码
} catch (SpecificException e) {
// 处理特定异常
} catch (Exception e) {
// 处理其他异常
} finally {
// 总是执行的代码
}
多重异常处理
public void processData(String data) {
try {
// 可能抛出多种异常的代码
int length = data.length(); // NullPointerException
int number = Integer.parseInt(data); // NumberFormatException
int result = 100 / number; // ArithmeticException
System.out.println("处理结果: " + result);
} catch (NullPointerException e) {
System.out.println("数据为null");
} catch (NumberFormatException e) {
System.out.println("数据格式不正确");
} catch (ArithmeticException e) {
System.out.println("除零操作");
} catch (Exception e) {
System.out.println("未知错误: " + e.getMessage());
} finally {
System.out.println("处理完成");
}
}
重要提示:catch块顺序:从子类异常到父类异常finally块:无论是否有异常都会执行异常传播:未捕获的异常会向上传播
异常处理最佳实践
好的做法
1. 处理具体的异常类型
// 好的做法
try {
int number = Integer.parseInt(input);
} catch (NumberFormatException e) {
// 处理具体异常
System.out.println("输入格式错误: " + input);
}
2. 提供有意义的异常信息
public void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException(
String.format("年龄不能为负数,当前值: %d", age)
);
}
}
3. 正确清理资源
InputStream input = null;
try {
input = new FileInputStream("file.txt");
// 使用资源
} catch (IOException e) {
System.out.println("文件操作异常: " + e.getMessage());
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
System.out.println("关闭文件异常: " + e.getMessage());
}
}
}
应该避免的做法
1. 捕获但忽略异常
// 错误做法
try {
riskyOperation();
} catch (Exception e) {
// 什么都不做 - 这是错误的!
}
2. 捕获过于宽泛的异常
// 错误做法
try {
specificOperation();
} catch (Exception e) {
// 太宽泛,应该捕获具体异常
}
3. 用异常控制程序流程
// 错误做法
try {
return array[index];
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
// 正确做法
if (index >= 0 && index < array.length) {
return array[index];
} else {
return null;
}
性能考虑
性能要点:异常处理有开销:异常对象创建需要生成堆栈跟踪不要用异常控制流程:异常应该用于异常情况避免频繁抛出异常:在循环中进行预检查
// 性能对比示例
// 坏做法:用异常控制流程(性能差)
for (int i = 0; i < 1000; i++) {
try {
if (i % 2 == 0) {
throw new Exception("偶数");
}
} catch (Exception e) {
// 处理偶数
}
}
// 好做法:用条件判断(性能好)
for (int i = 0; i < 1000; i++) {
if (i % 2 == 0) {
// 处理偶数
}
}
实战练习
练习建议:练习1:编写一个计算器程序,处理各种可能的异常练习2:实现一个文件读取工具,正确处理IO异常练习3:创建自定义异常类,实现业务异常处理练习4:分析开源项目中的异常处理模式
常见问题
Q: 什么时候应该捕获异常?A: 当你能够合理地处理异常或需要进行资源清理时。Q: 检查异常和运行时异常的区别?A: 检查异常必须在编译时处理,运行时异常可以选择处理。Q: 是否应该捕获Error?A: 通常不应该,Error表示严重的系统问题,程序无法恢复。Q: 如何选择抛出异常还是返回错误码?A: 异常用于异常情况,错误码用于预期的错误状态。
评论
0 条
还没有评论,先写一条吧。