异常消息须精准定位问题,明确“谁出错、在哪出错、为什么出错”,含关键上下文如userId=123,避免敏感信息与解决方案;必须链式传递cause,禁用裸new RuntimeException;自定义异常慎加字段,日志记录勿重复打印堆栈。

异常消息字符串必须能定位到具体问题
只写 "Error occurred" 这类泛化信息等于没抛。调用方无法判断是参数为空、远程服务超时,还是数据库连接失败。真实场景中,应明确写出「谁出错了、在哪出错、为什么出错」。
- 包含关键上下文:如
userId=123、url=https://api.example.com/v1/order、status=502 - 避免拼接敏感数据(如密码、token),可用占位符代替:
"Failed to decrypt token for user [REDACTED]" - 不要在消息里写解决方案(如
"Please check your network"),那是文档或日志分析系统的事
构造异常时优先使用带 cause 的重载方法
直接 new RuntimeException("xxx") 会丢失原始异常栈,导致排查时看不到底层 NPE 或 SQLTimeoutException。Java 所有标准异常都提供 Throwable(String message, Throwable cause) 构造器,必须用。
- 正确写法:
throw new ServiceException("Failed to process order " + orderId, e); - 错误写法:
throw new ServiceException("Failed to process order " + orderId);(丢掉了e) - 如果捕获的是受检异常但不想向上抛,至少用
new RuntimeException(e)包装,别吞掉
自定义异常类要谨慎添加额外字段
加 errorCode、httpStatus 看似方便,但容易让异常类承担职责过重——它本该描述「发生了什么」,而不是决定「怎么响应」或「怎么展示」。
- 仅当业务逻辑真正需要区分语义时才加字段,比如支付异常分
INSUFFICIENT_BALANCE和INVALID_CARD - 避免把 HTTP 状态码塞进异常类:
HttpStatus.UNAUTHORIZED是响应层的事,Service 层抛AuthenticationException就够了 - 如果加了字段,务必重写
toString()或getMessage(),确保日志里能打出来(默认不会自动包含)
日志记录异常时别重复打印 stack trace
用 log.error("msg", e) 就够了。如果再手动调用 e.printStackTrace() 或 log.error(e.getMessage(), e),会导致堆栈重复输出,干扰排查。
立即学习“Java免费学习笔记(深入)”;
- SLF4J / Logback 正确用法:
log.error("Order processing failed for orderId={}", orderId, e); - 错误模式:
log.error("Order processing failed: " + e.getMessage()); log.error("Stack trace:", e); - 注意:某些监控 SDK(如 Sentry)会自动采集未捕获异常,此时业务代码里主动
log.error可能造成重复上报










