目录
- 🚀前言
- 🤔异常的定义与分类
-
- 💯运行时异常
- 💯编译时异常
- 💯异常的基本处理
- 🌟异常的作用
- 🐧自定义异常
-
- 💯自定义运行时异常
- 💯自定义编译时异常
- ✍️异常的处理方案
-
- 💯方案一:异常捕获与用户友好提示
- 💯方案二:异常捕获与自动修复
🚀前言

大家好!我是 EnigmaCoder 。
本文介绍java的异常部分,从基础到自定义,最后到处理方案进行全解析。
🤔异常的定义与分类
定义:Java
中的异常是程序运行时发生的错误或意外情况,是Throwable
类的子类,用于封装程序执行过程中出现的不正常事件,可通过异常处理机制进行捕获和处理,分为可处理的Exception
(包括受检查异常和运行时异常)和不可处理的Error
(系统级错误)。
异常的分类
Java
异常体系基于Throwable
类,主要分为两大类:
Error
:系统级错误(如内存溢出OutOfMemoryError
),程序无法处理,也就是说系统一旦出现问题,sun
公司会把这些问题封装成Error
对象给出来。Exception
:指可被程序捕获和处理的异常,我们程序员通常会用Exception
以及它的孩子来封装程序出现的问题,其进一步分为:- 运行时异常(
RuntimeException
):RuntimeException
及其子类,编译阶段不会出现错误提示,运行时出现的异常。(如:数组索引越界异常) - 编译时异常(
Checked Exception
):编译阶段会出现错误提示。(如:日期解析异常)
- 运行时异常(
💯运行时异常
特点:编译阶段不会报错,运行时出现的异常,继承自RuntimeException
类。
示例:
java
public class Text {
public static void main(String[] args) {
int []arr={1,2,3,4,5};
System.out.println(arr[5]);
}
}
这是一段数组越界的的代码,编译时没有报错,运行时就会出现以下报错:

💯编译时异常
java
public class Text {
public static void main(String[] args) {
show();
}
public static void show(){
System.out.println("==程序开始==");
String str="2025-01-01 00:00:00";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=sdf.parse(str);
System.out.println(date);
System.out.println("==程序结束==");
}
}
报错信息如下:
💯异常的基本处理
-
抛出异常(
throws
)在方法上使用
throws
关键字,可以将方法内部出现的异常抛出去给调用者处理。
格式如下:
java
方法 throws 异常1,异常2,异常3 ...{
...
}
-
捕获异常(
try...catch
)直接捕获程序出现的异常。
格式如下:
java
try{
//监视可能出现异常的代码
}catch (异常类型1 变量){
//处理异常
}catch (异常类型2 变量){
//处理异常
}...
🌟异常的作用
- 作用1:异常是用来定位程序bug的关键信息
异常是程序运行过程中发生错误或异常情况时抛出的信号。它包含了丰富的调试信息,可以帮助开发者快速定位和修复问题。具体来说:
- 异常类型:指明错误的性质,如
NullPointerException
、ArrayIndexOutOfBoundsException
等。 - 异常信息:描述错误的具体原因,如"
Index 5 out of bounds for length 3
"。 - 堆栈跟踪:显示异常发生时的调用链,帮助定位问题发生的具体位置。
java
try {
int[] arr = new int[3];
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
// 输出:
// java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
// at Main.main(Main.java:5)
}
通过分析异常信息,开发者可以快速定位到数组越界的问题发生在
Main.java
文件的第5行。
- 作用2:可以作为方法内部的一种特殊返回值以便通知上层调用者,方法的执行问题
在方法设计中,异常机制提供了一种优雅的错误处理方式,相比传统的错误码返回具有以下优势:
- 强制处理:调用者必须处理或继续抛出检查型异常(Checked Exception)。
- 信息丰富:可以携带详细的错误描述和上下文信息。
- 代码解耦:将正常业务逻辑与错误处理逻辑分离,提高代码可读性。
应用场景示例:
java
public void transfer(Account from, Account to, double amount) throws InsufficientBalanceException {
if (from.getBalance() < amount) {
throw new InsufficientBalanceException("账户余额不足,当前余额:" + from.getBalance());
}
// 执行转账逻辑
}
// 调用方法
try {
transfer(accountA, accountB, 1000);
} catch (InsufficientBalanceException e) {
System.out.println("转账失败:" + e.getMessage());
// 输出:转账失败:账户余额不足,当前余额:500.0
}
通过抛出异常,
transfer
方法清晰地表达了业务约束,调用方可以针对性地处理特定错误情况,而不是依赖模糊的错误码或布尔返回值。
🐧自定义异常
java
无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了。
💯自定义运行时异常
- 定义一个异常类继承
RuntimeException
。
java
public class CustomRuntimeException extends RuntimeException {
// 类体
}
- 重写构造器
java
public class CustomRuntimeException extends RuntimeException {
// 无参构造器
public CustomRuntimeException() {
super();
}
// 带消息的构造器
public CustomRuntimeException(String message) {
super(message);
}
// 带消息和原因的构造器
public CustomRuntimeException(String message, Throwable cause) {
super(message, cause);
}
// 带原因的构造器
public CustomRuntimeException(Throwable cause) {
super(cause);
}
}
- 通过
throw new
异常类(xxx)来创建异常对象并抛出。
java
public void someMethod(int value) {
if (value < 0) {
throw new CustomRuntimeException("Value cannot be negative");
}
// 其他逻辑
}
特点:编译阶段不报错,运行时才可能出现。
💯自定义编译时异常
- 定义一个异常类继承
Exception
。
java
public class CustomCompileException extends Exception {
// 异常类的具体实现
}
- 重写构造器。
java
public class CustomCompileException extends Exception {
// 无参构造器
public CustomCompileException() {
super();
}
// 带有错误信息的构造器
public CustomCompileException(String message) {
super(message);
}
// 带有错误信息和原因的构造器
public CustomCompileException(String message, Throwable cause) {
super(message, cause);
}
// 仅带有原因的构造器
public CustomCompileException(Throwable cause) {
super(cause);
}
}
- 通过
throw new
异常类(xxx)创建异常对象并抛出。
java
public void validateInput(String input) throws CustomCompileException {
if (input == null || input.isEmpty()) {
throw new CustomCompileException("输入不能为空");
}
// 其他逻辑
}
特点:编译阶段就报错,提醒十分激进。
✍️异常的处理方案
💯方案一:异常捕获与用户友好提示
底层异常层层往上抛出,最外层捕获异常,记录下异常信息,并响应适合用户观看的信息进行提示。
处理流程:
-
异常抛出:在底层代码中,当检测到异常情况时,使用
throw
关键字抛出异常对象。例如,在数据库操作中,如果查询失败,可以抛出SQLException
。 -
异常传递:异常会沿着调用栈向上传递,直到被捕获。中间层代码可以选择不处理异常,继续向上抛出。例如,在服务层捕获
SQLException
后,可以将其包装为自定义的业务异常BusinessException
并继续抛出。 -
最外层捕获:在应用的最外层(如控制器层或主函数)使用
try-catch
块捕获所有异常。例如,在Spring MVC
的控制器中,可以使用@ExceptionHandler
注解来统一处理异常。 -
记录日志:捕获异常后,使用日志框架(如
Log4j
、SLF4J
)记录异常的详细信息,包括异常类型、堆栈轨迹、发生时间等。这有助于后续的问题排查和分析。 -
用户提示:根据异常类型,生成适合用户观看的提示信息。例如,对于网络超时异常,可以提示"网络连接不稳定,请稍后重试";对于权限不足异常,可以提示"您没有权限执行此操作"。
示例代码:
java
try {
// 业务逻辑代码
} catch (BusinessException e) {
logger.error("业务异常: ", e);
return new Response("操作失败,请检查输入是否正确");
} catch (Exception e) {
logger.error("系统异常: ", e);
return new Response("系统繁忙,请稍后重试");
}
应用场景:
- Web 应用中的全局异常处理
- 微服务架构中的服务间调用异常处理
- 桌面应用程序中的用户操作异常处理
💯方案二:异常捕获与自动修复
最外层捕获异常后,尝试重新修复。
处理流程:
-
异常捕获:在最外层代码中捕获异常,与方案一类似。
-
异常分析:根据异常类型和上下文信息,判断是否可以进行自动修复。例如,对于网络连接异常,可以尝试重新连接;对于文件读取异常,可以尝试从备份文件读取。
-
修复尝试:
- 重试机制:对于暂时性异常(如网络抖动),可以设置重试次数和间隔时间,多次尝试执行操作。
- 备用方案:对于无法直接修复的异常,可以切换到备用方案。例如,主数据库不可用时,切换到备用数据库。
- 资源清理:在修复过程中,可能需要释放已占用的资源,如关闭文件句柄、释放数据库连接等。
-
结果处理:
- 如果修复成功,继续正常执行后续逻辑。
- 如果修复失败,记录日志并按照方案一的方式向用户提示错误信息。
示例代码:
java
int retryCount = 3;
while (retryCount > 0) {
try {
// 可能失败的操作
performOperation();
break; // 成功则退出循环
} catch (NetworkException e) {
retryCount--;
if (retryCount == 0) {
logger.error("网络连接失败,重试次数用尽");
return new Response("网络连接失败,请检查网络设置");
}
Thread.sleep(1000); // 等待1秒后重试
}
}
应用场景:
- 分布式系统中的容错处理
- 实时数据处理系统的故障恢复
- 自动化运维系统中的错误自愈
注意事项:
- 自动修复可能会掩盖潜在的系统问题,应谨慎使用
- 需要设置合理的重试次数和间隔时间,避免无限重试
- 对于关键业务操作,建议在自动修复后仍进行人工确认
通过以上两种方案,可以有效地处理系统中的异常情况,提高系统的稳定性和用户体验。具体选择哪种方案,需要根据业务场景和异常类型来决定。