Java异常处理从入门到实战
1. 什么是异常?为什么要处理异常?
在程序运行过程中,总会遇到一些意料之外的情况:用户输入了非法数据、网络突然断开、文件找不到、数据库连接超时……这些在 Java 中统称为异常(Exception)。
如果不处理异常,程序会直接崩溃退出,用户看到的将是满屏的红色错误堆栈——这对用户体验来说是灾难性的。因此,Java 提供了一套完善的异常处理机制,让我们能够:
- 捕获异常:在异常发生时拦截它,不让程序崩溃
- 处理异常:给出友好的提示、记录日志、尝试恢复
- 清理资源:确保文件流、网络连接等资源被正确关闭
2. Java异常体系结构
Java 的异常类都继承自 Throwable,主要分为两大类:
Throwable
├── Error(错误)—— 程序无法处理
└── Exception(异常)
├── RuntimeException(运行时异常)—— 不强制处理,如 NullPointerException
└── 非运行时异常(Checked Exception)—— 必须处理
新手最容易混淆的点:RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)可以不写 try-catch,编译器不会报错;而 IOException 这类 Checked Exception,编译器强制你处理,否则无法通过编译。
3. 异常处理的核心语法
3.1 try-catch 基本结构
try {
// 可能发生异常的代码
} catch (异常类型 变量名) {
// 异常发生时的处理代码
}
3.2 多重 catch
一个 try 块后面可以跟多个 catch 块,分别捕获不同类型的异常。顺序为:子类异常在前,父类异常在后。
try {
// 可能抛出多种异常的代码
} catch (FileNotFoundException e) {
System.out.println("文件未找到");
} catch (IOException e) {
System.out.println("IO异常");
} catch (Exception e) {
System.out.println("其他异常");
}
3.3 finally 块——资源清理的保障
finally 块中的代码无论是否发生异常,都会执行。这保证了资源释放操作不会被跳过。
try {
// 使用资源
} catch (Exception e) {
// 处理异常
} finally {
// 关闭资源(一定会执行)
}
3.4 完整执行流程
try → 无异常 → 跳过 catch → 执行 finally
try → 有异常 → 进入匹配的 catch → 执行 finally
try → 有异常但未捕获 → 执行 finally → 异常继续向上抛
4. 自定义异常
当 Java 内置的异常类型无法准确描述业务问题时,我们可以创建自己的异常类。
4.1 为什么要自定义异常?
语义更清晰;便于精准捕获:可以针对特定业务异常做不同处理;便于日志和监控:按异常类型统计错误分布。
4.2 如何自定义异常?
// 继承 Exception 就是 Checked Exception(必须处理)
class MyBusinessException extends Exception {
public MyBusinessException(String message) {
super(message);
}
}
// 继承 RuntimeException 就是运行时异常(可选处理)
class MyRuntimeBusinessException extends RuntimeException {
public MyRuntimeBusinessException(String message) {
super(message);
}
}
新手建议:在支付、文件操作等需要强制调用方处理的场景,继承 Exception;在参数校验等内部逻辑场景,继承 RuntimeException。
5. throws 与 throw 关键字
5.1 throw——主动抛出异常
public void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
5.2 throws——声明可能抛出的异常
如果方法内部抛出了 Checked Exception 但不想在当前方法中处理,可以用 throws 声明,让调用者去处理。
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
// ...
}
6. 实战案例:支付网关异常处理
现在我们把前面学到的知识综合起来,模拟一个电商支付场景。
6.1 需求分析
支付过程中可能遇到三种典型故障:
- 网络超时——临时性问题,可以重试
- 支付被拒绝——余额不足、风控拦截,需要提示用户
- 系统内部错误——需要记录日志并告警
6.2 第一步:自定义异常类
/**
* 网络超时异常
*/
class NetworkTimeoutException extends Exception {
public NetworkTimeoutException() {
super("Network Timeout");
}
}
/**
* 支付被拒绝异常(如余额不足)
*/
class PaymentDeclinedException extends Exception {
public PaymentDeclinedException() {
super("Payment Declined");
}
}
/**
* 支付系统内部错误异常
*/
class SystemErrorException extends Exception {
public SystemErrorException() {
super("System Error");
}
}
6.3 第二步:编写支付网关类
/**
* 支付网关模拟类
*/
class PaymentGateway {
public void connect() {
System.out.println("Gateway Connected");
}
public void disconnect() {
System.out.println("Gateway Disconnected");
}
/**
* 处理支付
* @param amount 支付金额
* @param errorCode 错误码(0-成功,1-网络超时,2-支付拒绝,3-系统错误)
*/
public void processPayment(double amount, int errorCode)
throws NetworkTimeoutException, PaymentDeclinedException, SystemErrorException {
switch (errorCode) {
case 0:
System.out.printf("Payment Success: %.2f%n", amount);
break;
case 1:
throw new NetworkTimeoutException();
case 2:
throw new PaymentDeclinedException();
case 3:
throw new SystemErrorException();
default:
throw new IllegalArgumentException("Invalid error code: " + errorCode);
}
}
}
6.4 第三步:主程序
public class Main {
public static void main(String[] args) {
PaymentGateway gateway = new PaymentGateway();
// 1. 建立连接
gateway.connect();
try {
// 2. 尝试支付(模拟网络超时)
gateway.processPayment(199.99, 1);
} catch (NetworkTimeoutException e) {
System.out.println("捕获异常: " + e.getMessage());
System.out.println("→ 建议:稍后重试");
} catch (PaymentDeclinedException e) {
System.out.println("捕获异常: " + e.getMessage());
System.out.println("→ 建议:检查余额或支付方式");
} catch (SystemErrorException e) {
System.out.println("捕获异常: " + e.getMessage());
System.out.println("→ 建议:联系客服");
} finally {
// 3. 无论成功还是失败,都必须断开连接
gateway.disconnect();
System.out.println("资源已清理,连接已关闭");
}
}
}
6.5 运行结果
Gateway Connected
捕获异常: Network Timeout
→ 建议:稍后重试
Gateway Disconnected
资源已清理,连接已关闭
异常处理不是可有可无的"装饰",而是生产级代码的基本素养。希望你能在每一个项目中,都认真对待异常处理,写出更健壮、更可靠的 Java 程序。
更多推荐
所有评论(0)