本文目录导读:
自定义异常类是 Java(和其他面向对象语言)中非常常见的需求,用于更精确地表达业务逻辑中的错误。
下面以 Java 为例,详细讲解如何自定义异常类,包括步骤、最佳实践和完整示例。
核心步骤(以 Java 为例)
-
继承一个合适的异常类:
- 通常继承
Exception(检查型异常,必须处理)或RuntimeException(非检查型异常,可选的运行时异常)。 - 如何选择?
- 如果调用方必须处理这个异常(余额不足导致转账失败),则继承
Exception。 - 如果调用方可以选择不处理(参数校验失败、数组越界),通常继承
RuntimeException(Spring 等框架常用这种方式)。
- 如果调用方必须处理这个异常(余额不足导致转账失败),则继承
- 通常继承
-
提供构造方法:
- 至少提供无参构造。
- 提供带
String message的构造(错误描述)。 - 提供带
String message和Throwable cause的构造(保留原始异常链)。 - 提供带
Throwable cause的构造(仅保留原因)。
完整代码示例
假设我们正在开发一个电商系统,需要自定义一个 InsufficientStockException(库存不足异常)。
// 1. 继承 RuntimeException(非检查型异常,调用方可以不用每次都 try-catch)
public class InsufficientStockException extends RuntimeException {
// 2. 定义一个额外的业务字段(可选),比如当前库存量
private final int currentStock;
// 3. 提供多个构造方法
// 无参构造
public InsufficientStockException() {
super("库存不足");
this.currentStock = 0;
}
// 带消息的构造
public InsufficientStockException(String message) {
super(message);
this.currentStock = 0;
}
// 带消息和库存量的构造(最常用的业务异常构造)
public InsufficientStockException(String message, int currentStock) {
super(message);
this.currentStock = currentStock;
}
// 带消息和原始原因的构造
public InsufficientStockException(String message, Throwable cause) {
super(message, cause);
this.currentStock = 0;
}
// 带完整信息的构造
public InsufficientStockException(String message, int currentStock, Throwable cause) {
super(message, cause);
this.currentStock = currentStock;
}
// 4. 提供 getter 方法,方便调用方获取额外信息
public int getCurrentStock() {
return currentStock;
}
}
使用自定义异常
public class OrderService {
public void createOrder(Product product, int quantity) {
// 模拟业务逻辑
if (product.getStock() < quantity) {
// 抛出自定义异常,并传递当时的库存量
throw new InsufficientStockException(
"商品 [" + product.getName() + "] 库存不足,需求: " + quantity + ",当前库存: " + product.getStock(),
product.getStock()
);
}
// 正常下单逻辑...
}
}
调用方处理:
public class OrderController {
public void buyProduct(Long productId, int quantity) {
try {
orderService.createOrder(product, quantity);
} catch (InsufficientStockException e) {
// 可以获取自定义的字段
int currentStock = e.getCurrentStock();
System.err.println("下单失败: " + e.getMessage());
// 返回友好的错误码给前端,HTTP 400,并附带当前库存
}
}
}
最佳实践与建议
-
命名规范:以
Exception准确描述错误类型。- 好:
InsufficientStockException、UserNotFoundException、InvalidParameterException - 不好:
MyException、ServiceException(过于宽泛)
- 好:
-
区分异常类型:
- 业务异常(如余额不足、库存不足):通常继承
RuntimeException,配合全局异常处理器(如@ControllerAdvice)统一返回错误信息给前端。 - 系统异常(如 IO 错误、数据库连接失败):通常继承
Exception,强制调用方处理或向上抛出。
- 业务异常(如余额不足、库存不足):通常继承
-
不要为了自定义而自定义:
- JDK 已有的异常类型(如
IllegalArgumentException、IllegalStateException等)可以清楚表达错误,优先使用内置的,不要重复造轮子。
- JDK 已有的异常类型(如
-
保留异常链:
- 一定要提供带
Throwable cause的构造方法,这样在日志中可以看到完整的异常堆栈,便于排查问题。
- 一定要提供带
-
携带业务上下文(可选但推荐):
- 如上例中的
currentStock,可以帮助调用方或前端做出更精确的提示。
- 如上例中的
其他语言示例(Python)
class InsufficientStockError(Exception):
def __init__(self, message="库存不足", current_stock=0):
self.current_stock = current_stock
super().__init__(message)
自定义异常类的核心公式是:
继承一个异常基类 + 提供多个构造方法 + 可选的业务字段 + 清晰的命名
这样,你的代码在出错时就能传递更精准的语义信息,让调试和维护变得更容易。
标签: 异常处理机制