第一部分:Java异常体系回顾
理解异常继承结构,为自定义异常打下基础
学习目标
掌握Java异常体系结构,区分受检异常和非受检异常
异常类层次结构
Throwable
├── Error (严重错误,程序不应处理)
└── Exception
├── RuntimeException (非受检异常)
└── 其他Exception子类 (受检异常)
异常分类
- 受检异常(Checked):必须处理或声明
- 非受检异常(Unchecked):RuntimeException及其子类
- Error:JVM相关问题,通常不处理
第二部分:为什么需要自定义异常
了解自定义异常的业务价值和应用场景
使用场景
- 业务规则违反(如余额不足)
- 特定领域错误(如用户不存在)
- 需要额外信息的异常
- 区分系统异常和业务异常
优势
- 提高代码可读性
- 精确的错误处理
- 便于日志记录和调试
- 支持国际化错误消息
第三部分:创建受检异常类
继承Exception创建必须处理的异常类型
1
创建基础自定义异常
创建一个简单的受检异常,继承Exception类:
public class BusinessException extends Exception {
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
必须提供所有四个标准构造方法
2
添加额外字段
为异常添加错误代码等额外信息:
public class UserNotFoundException extends Exception {
private final String userId;
private final int errorCode;
public UserNotFoundException(String userId) {
super("用户不存在: " + userId);
this.userId = userId;
this.errorCode = 404;
}
public String getUserId() {
return userId;
}
public int getErrorCode() {
return errorCode;
}
}
3
使用自定义异常
在服务类中使用自定义异常:
public class UserService {
public User findUser(String userId) throws UserNotFoundException {
if (userId == null || userId.trim().isEmpty()) {
throw new UserNotFoundException(userId);
}
// 模拟数据库查找
User user = database.findById(userId);
if (user == null) {
throw new UserNotFoundException(userId);
}
return user;
}
}
第四部分:创建非受检异常类
继承RuntimeException创建无需强制处理的异常
1
创建运行时异常
继承RuntimeException创建非受检异常:
public class ValidationException extends RuntimeException {
private final String field;
private final Object value;
public ValidationException(String field, Object value) {
super(String.format("字段 '%s' 的值 '%s' 无效", field, value));
this.field = field;
this.value = value;
}
public String getField() {
return field;
}
public Object getValue() {
return value;
}
}
2
使用场景示例
在参数验证中使用非受检异常:
public class UserValidator {
public void validateEmail(String email) {
if (email == null || !email.contains("@")) {
throw new ValidationException("email", email);
}
}
public void validateAge(int age) {
if (age < 0 || age > 150) {
throw new ValidationException("age", age);
}
}
}
第五部分:自定义异常最佳实践
遵循行业标准,编写高质量的异常类
命名规范
- 以"Exception"结尾
- 使用描述性名称(如InvalidOrderStateException)
- 避免使用过于通用的名称
- 与业务领域保持一致
设计原则
- 提供所有四个标准构造方法
- 添加有用的额外信息
- 使异常不可变(final字段)
- 考虑序列化支持
高级技巧
使用异常链保留原始异常信息:
try {
// 数据库操作
} catch (SQLException e) {
throw new DataAccessException("数据库访问失败", e);
}
第六部分:常见错误与解决方案
避免开发中的典型陷阱
错误1:过度使用受检异常
错误示例:
// 错误:为业务校验使用受检异常
public void validateUser(User user) throws InvalidUserException {
if (user.getAge() < 18) {
throw new InvalidUserException("用户未满18岁");
}
}
解决方案:使用非受检异常
错误2:丢失异常信息
错误示例:
// 错误:丢失了原始异常信息
try {
// 操作
} catch (Exception e) {
throw new BusinessException("操作失败"); // 丢失了原始异常
}
正确做法:
throw new BusinessException("操作失败", e);
错误3:异常过于具体
错误示例:
// 过度设计:为每个验证规则创建单独异常
public class EmailTooShortException extends ValidationException {}
public class EmailNoAtSignException extends ValidationException {}
解决方案:使用带有错误代码的统一异常
第七部分:完整实战案例
从零开始构建一个订单处理的异常体系
1
定义基础异常
public abstract class OrderException extends Exception {
private final String orderId;
protected OrderException(String orderId, String message) {
super(message);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
2
创建具体异常类
public class OrderNotFoundException extends OrderException {
public OrderNotFoundException(String orderId) {
super(orderId, "订单不存在: " + orderId);
}
}
public class InsufficientStockException extends OrderException {
private final String productId;
private final int requested;
private final int available;
public InsufficientStockException(String orderId, String productId,
int requested, int available) {
super(orderId, String.format(
"商品 %s 库存不足,需求: %d,可用: %d",
productId, requested, available));
this.productId = productId;
this.requested = requested;
this.available = available;
}
// getters...
}
3
实现订单服务
public class OrderService {
public void processOrder(String orderId) throws OrderException {
Order order = findOrder(orderId);
validateOrder(order);
checkInventory(order);
// 处理订单...
}
private Order findOrder(String orderId) throws OrderNotFoundException {
Order order = orderRepository.findById(orderId);
if (order == null) {
throw new OrderNotFoundException(orderId);
}
return order;
}
private void checkInventory(Order order) throws InsufficientStockException {
for (OrderItem item : order.getItems()) {
int available = inventoryService.getStock(item.getProductId());
if (available < item.getQuantity()) {
throw new InsufficientStockException(
order.getId(),
item.getProductId(),
item.getQuantity(),
available);
}
}
}
}
动手练习
- 为一个银行系统创建自定义异常:InsufficientFundsException
- 添加账户ID、当前余额、提款金额等字段
- 实现转账方法,使用这个异常
- 创建全局异常处理器
- 添加单元测试验证异常行为
知识总结
回顾自定义异常的核心要点
关键概念
- 受检异常 vs 非受检异常
- 异常链的重要性
- 异常信息的丰富性
- 异常类的设计模式
检查清单
- ✅ 异常类命名规范
- ✅ 提供完整构造方法
- ✅ 添加有用的额外信息
- ✅ 正确使用异常链
- ✅ 文档化异常使用场景