Java异常处理核心知识点总结(学习笔记)
在Java编程中,异常处理是保证程序健壮性的关键机制。它能让程序在遇到错误时不会直接崩溃,而是优雅地处理问题。本文结合课堂代码,系统梳理Java异常的体系结构、分类、处理方式及自定义异常,补充关键细节和最佳实践。
一、异常的体系结构
Java中所有的异常和错误都继承自Throwable类,它是异常体系的根节点。整体结构如下:
Throwable
├── Error(错误)
│ ├── StackOverflowError(栈溢出)
│ └── OutOfMemoryError(堆溢出)
└── Exception(异常)
├── RuntimeException(运行时异常/非受检异常)
│ ├── NullPointerException(空指针异常)
│ ├── ArrayIndexOutOfBoundsException(数组越界异常)
│ ├── ClassCastException(类型转换异常)
│ └── ArithmeticException(算术异常)
└── 其他Exception(非运行时异常/受检异常)
├── FileNotFoundException(文件未找到异常)
├── IOException(IO异常)
└── SQLException(数据库异常)
1. Error(错误)
Error是程序无法处理的严重问题,通常由JVM底层抛出,程序只能尽量避免。
- StackOverflowError:栈溢出,常见于递归调用过深(如无限递归)。
- OutOfMemoryError:堆溢出,常见于创建过大的对象或内存泄漏。
2. Exception(异常)
Exception是程序可以处理的错误,分为两类:
- 运行时异常(非受检异常):由程序逻辑错误引起,编译时不强制要求处理。例如空指针、数组越界。
- 非运行时异常(受检异常):编译时必须处理,否则程序无法通过编译。例如文件未找到、IO异常。
二、异常的处理方式
Java提供了两种核心的异常处理方式:try-catch-finally(捕获处理)和throws(向上抛出)。
1. try-catch-finally:捕获并处理异常
这是最常用的异常处理方式,结构如下:
java
try {
// 可能抛出异常的代码
} catch (异常类型1 e) {
// 处理异常类型1
} catch (异常类型2 e) {
// 处理异常类型2(子类异常在前,父类在后)
} finally {
// 无论是否发生异常,都会执行的代码(通常用于关闭资源)
}
执行流程
- 先执行
try块中的代码。 - 如果
try块抛出异常,跳转到匹配的catch块处理。 - 最后执行
finally块(即使try或catch中有return,finally也会执行)。
代码示例(结合课堂代码)
java
public class Main {
public static void main(String[] args) {
run();
}
public static void run() {
try {
// 手动抛出一个异常
throw new Exception("自定义异常");
} catch (Exception e) {
// 捕获并处理异常,打印异常信息
System.out.println(e.getMessage());
}
}
}
注意事项
- 多个catch块的顺序:子类异常必须写在父类异常前面,否则父类会把子类异常"拦截"掉。
- finally的特殊情况 :如果
try块中执行了System.exit(0),finally块不会执行(因为JVM直接退出了)。 - 资源关闭 :Java 7+推荐使用
try-with-resources自动关闭资源,更简洁安全。
2. throws:向上抛出异常
如果当前方法不想处理异常,可以使用throws关键字将异常抛给调用者处理。
java
// 方法声明中使用throws,抛出多个异常用逗号分隔
public static void main(String[] args) throws Exception {
// 调用run(),run()抛出的异常由main()的调用者(JVM)处理
run();
}
public static void run() throws Exception {
// 不处理异常,直接抛出
throw new Exception("自定义异常");
}
throws和throw的区别
| 关键字 | 位置 | 作用 |
|---|---|---|
throws |
方法声明上 | 表示该方法可能抛出异常,由调用者处理 |
throw |
方法体内 | 手动抛出一个具体的异常对象 |
三、自定义异常
Java内置的异常类可能无法满足所有业务场景,我们可以通过继承Exception或RuntimeException来自定义异常。
自定义异常的步骤
- 定义一个类,继承
Exception(受检异常)或RuntimeException(运行时异常)。 - 提供构造方法(无参构造和带异常信息的构造)。
完整代码示例
java
// 1. 自定义受检异常(继承Exception)
public class BusinessException extends Exception {
// 无参构造
public BusinessException() {}
// 带异常信息的构造
public BusinessException(String message) {
super(message);
}
}
// 2. 使用自定义异常
public class CustomExceptionTest {
public static void main(String[] args) {
try {
checkAge(-1);
} catch (BusinessException e) {
System.out.println("捕获到业务异常:" + e.getMessage());
}
}
// 检查年龄的方法,年龄不能为负数
public static void checkAge(int age) throws BusinessException {
if (age < 0) {
// 抛出自定义异常
throw new BusinessException("年龄不能为负数:" + age);
}
System.out.println("年龄合法:" + age);
}
}
四、最佳实践总结
- 优先捕获具体异常 :不要直接捕获
Exception,应该捕获具体的子类异常,便于定位问题。 - 不要忽略异常 :
catch块中至少要打印异常信息(e.printStackTrace()),不要留空。 - 使用try-with-resources :处理IO流等资源时,优先使用
try-with-resources自动关闭资源。 - 自定义异常要有意义 :自定义异常类名要能体现业务场景(如
BusinessException、UserNotFoundException)。
异常处理是Java编程的基本功,掌握它能让你的程序更健壮、更易维护。多写多练,结合实际场景理解异常的处理逻辑,就能熟练运用啦。