该如何正确处理异常信息

1318 字
7 分钟
该如何正确处理异常信息

异常处理是 Java 开发中非常重要的一个知识点。

很多新人在处理异常的时候,要么不知道什么时候该 try-catch、什么时候该抛出异常,要么 try-catch 里面什么都不做,直接把异常”吞掉”。

这两种做法都是不对的。

什么时候抛异常、什么时候 try-catch 处理异常,然后 try-catch 处理异常该怎么处理?


一、什么时候 try-catch#

当你不想让调用方察觉到异常时,就用 try-catch 悄悄接住:只把日志写足,别把异常抛出去。

同样,当你不想让一次异常中断整个流程、导致后续代码无法运行时,也用 try-catch 把它拦下来。

更进一步,把 try-catch 当成异常场景下的”降级开关”:一旦出事,立刻捕获,记录日志、返回默认值或缓存数据,让用户依旧能看到页面,让业务流程继续走下去。

1、隐藏异常,不让调用者感知异常,并做降级处理#

典型的场景是:一个页面有核心数据和推荐数据两部分,推荐数据来自一个不太稳定的外部服务。如果推荐服务挂了,不应该让整个页面都报错,而是返回空列表,确保核心功能正常。

@Service
@Slf4j
public class ProductService {
@Autowired
private RecommendService recommendService; // 可能不稳定的推荐服务
public ProductDetail getProductDetail(Long productId) {
ProductDetail detail = productDao.getById(productId);
try {
// 获取商品推荐列表(非核心功能)
List<Product> recommendations = recommendService.getRecommendations(productId);
detail.setRecommendations(recommendations);
} catch (Exception e) {
// 核心:捕获所有异常,确保详情页主功能不受影响
log.warn("获取商品[{}]推荐列表失败,将返回空推荐列表", productId, e);
detail.setRecommendations(Collections.emptyList()); // 降级:返回空列表
}
return detail; // 主商品信息一定正常返回
}
}

这种写法的关键是:日志要写足,降级逻辑要合理,不要什么都不做。

2、对原始异常进行包装#

当需要对原始异常信息进行包装的时候,也需要使用 try-catch。

这个时候在 try-catch 里面抛出的异常,应该是自定义异常。因为自定义异常返回的信息是清晰明了的,能告诉用户目前出现了什么情况,而不是直接把原始异常信息暴露给用户。

直接把原始异常信息暴露出去,有两个问题:

  • 用户体验差:用户看到一堆技术栈信息,完全不知道发生了什么
  • 安全隐患:原始异常可能包含数据库 IP、表名、SQL 语句等敏感信息

错误的示例:

public User login(String username, String password) throws SQLException {
// 直接调用 DAO,可能抛出 SQLException
return userDao.findByUsernameAndPassword(username, password);
}

如果数据库连接失败,前端可能收到包含数据库 IP、表名、SQL 语句片段的错误信息,体验极差且不安全。

正确的做法:

// 1. 首先,定义一个业务异常
public class AuthenticationException extends RuntimeException {
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
// 2. 在 Service 层进行包装
public User login(String username, String password) {
try {
User user = userDao.findByUsernameAndPassword(username, password);
return user;
} catch (Exception e) { // Spring 对 SQLException 等的包装
// 1. 关键:记录原始异常,供后续排查
log.error("用户[{}]登录时数据访问异常", username, e);
// 2. 关键:抛出一个对上游和用户友好的业务异常
throw new AuthenticationException("系统繁忙,请稍后再试!");
}
}

这种写法的关键是:原始异常记到日志里,给用户和上游抛出友好的业务异常。


二、什么时候抛出异常#

在程序执行过程中,遇到核心业务前提条件不满足关键流程出现不可恢复的错误,导致后续所有操作变得无意义或不可能成功时,此时就应该抛出异常并终止当前执行流。

简单来说:条件不满足就别往下走了,直接抛异常。

public void payOrder(Order order, Payment payment) {
// 条件1:订单状态检查
if (!"PENDING".equals(order.getStatus())) {
// 后续支付逻辑已无意义,立即抛出!
throw new IllegalOrderStateException(
"订单[id=" + order.getId() + "]当前状态为" + order.getStatus() + ",无法支付。"
);
}
// 条件2:支付金额校验
if (payment.getAmount().compareTo(order.getTotalAmount()) != 0) {
// 金额不匹配,支付必然失败,立即抛出!
throw new PaymentAmountMismatchException(
"支付金额[" + payment.getAmount() + "]与订单总额[" + order.getTotalAmount() + "]不符。"
);
}
// 条件3:执行核心支付(只有所有前提满足,才执行到这里)
try {
paymentGateway.execute(payment);
} catch (BankGatewayException e) {
// 支付本身失败,同样抛出业务异常
throw new PaymentFailedException("银行支付处理失败,请重试");
}
// 更新状态(只有一切成功,才执行到这里)
order.setStatus("PAID");
orderRepository.update(order);
}

抛异常的核心原则:后续代码已经没有意义执行了,就立刻抛出,不要继续往下走。


三、总结#

场景处理方式关键点
非核心功能异常try-catch + 降级日志写足,返回默认值
原始异常不友好try-catch + 自定义异常记录原始异常,抛出友好信息
核心前提不满足直接抛出异常后续无意义,立即终止
不可恢复的错误直接抛出异常配合业务异常类使用

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
该如何正确处理异常信息
https://study-docs-158.pages.dev/posts/该如何正确处理异常信息/
作者
我的学习小铺
发布于
2026-08-01
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
我的学习小铺
学而时习之
分类
标签
最新动态
站点统计
文章
41
分类
7
标签
3
总字数
122,278
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.15.3
文章许可
CC BY-NC-SA 4.0