在Java开发中,当内置的异常类型无法准确描述业务场景中的错误情况时,我们就需要定义自定义异常并在方法中抛出,以此更精准地反馈程序运行中的问题。

自定义异常的定义
Java中的自定义异常需要继承现有的异常类,通常分为两种:继承Exception的受检异常,和继承RuntimeException的非受检异常。受检异常要求调用方必须处理,非受检异常则不需要强制处理。
下面是一个简单的自定义受检异常示例:
// 自定义受检异常,继承Exception
public class BusinessException extends Exception {
// 无参构造方法
public BusinessException() {
super();
}
// 带错误信息的构造方法
public BusinessException(String message) {
super(message);
}
// 带错误信息和原因的构造方法
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
如果需要定义非受检异常,只需要将上述代码中的extends Exception改为extends RuntimeException即可。
在方法中抛出自定义异常的步骤
在方法中抛出自定义异常需要遵循以下流程:
- 首先定义好对应的自定义异常类
- 在方法内部判断触发异常的业务条件
- 使用
throw关键字创建自定义异常实例并抛出 - 如果是受检异常,还需要在方法声明处使用
throws关键字声明抛出的异常类型
抛出受检自定义异常示例
下面的方法实现了用户年龄校验的逻辑,当年龄小于0或者大于150时,抛出我们定义的BusinessException:
public class UserService {
// 方法声明处用throws声明抛出的受检异常
public void checkUserAge(int age) throws BusinessException {
if (age < 0) {
// 年龄小于0时抛出自定义异常,携带错误信息
throw new BusinessException("用户年龄不能为负数");
}
if (age > 150) {
// 年龄大于150时抛出自定义异常,携带错误信息和原因
throw new BusinessException("用户年龄超出合理范围", new IllegalArgumentException("输入年龄:" + age));
}
System.out.println("用户年龄校验通过");
}
}
抛出非受检自定义异常示例
如果自定义异常继承的是RuntimeException,那么不需要在方法声明处用throws声明,调用方也可以不强制处理:
// 自定义非受检异常
public class ParamIllegalException extends RuntimeException {
public ParamIllegalException(String message) {
super(message);
}
}
public class OrderService {
public void createOrder(int count) {
if (count <= 0) {
// 直接抛出非受检自定义异常
throw new ParamIllegalException("订单商品数量必须大于0");
}
System.out.println("订单创建成功,商品数量:" + count);
}
}
throw和throws的区别
很多开发者会混淆throw和throws的用法,两者的核心区别如下:
| 对比项 | throw | throws |
|---|---|---|
| 作用位置 | 方法内部 | 方法声明处 |
| 作用 | 主动抛出具体的异常实例 | 声明方法可能抛出的异常类型,提醒调用方处理 |
| 后面跟的内容 | 异常对象实例,比如new BusinessException("错误") | 异常类的类名,多个异常用逗号分隔 |
| 执行效果 | 执行后方法会立即终止,跳转到异常处理逻辑 | 只是声明,不会主动触发异常 |
注意事项
- 自定义异常的类名要尽量清晰,能够直接体现异常的含义,比如
UserNotFoundException、OrderPaidException等 - 抛出异常时要携带清晰的错误信息,方便后续排查问题,尽量不要抛出没有错误描述的异常
- 受检异常适合必须让调用方处理的场景,比如业务规则校验失败;非受检异常适合程序运行时的非法参数、状态错误等场景
- 不要在
try块中只抛出自定义异常而不做任何处理,除非你确定调用方会处理该异常
调用方的异常处理
当方法抛出了受检异常时,调用方必须处理该异常,要么继续用throws声明抛出,要么用try-catch捕获处理:
public class Test {
public static void main(String[] args) {
UserService userService = new UserService();
try {
userService.checkUserAge(-5);
} catch (BusinessException e) {
// 捕获自定义异常,打印错误信息
System.out.println("捕获到业务异常:" + e.getMessage());
// 可以打印异常堆栈,方便排查问题
e.printStackTrace();
}
OrderService orderService = new OrderService();
// 非受检异常可以不强制捕获,但是建议根据业务场景处理
try {
orderService.createOrder(0);
} catch (ParamIllegalException e) {
System.out.println("捕获到参数异常:" + e.getMessage());
}
}
}