java异常自带错误信息,应选对类型、构造方式和抛出时机;用getmessage()和getcause()读取,避免手动拼接丢失堆栈;自定义异常只重写构造函数,勿覆写getmessage();异常不可在finally或异步中吞掉。

Java中异常本身就能携带错误信息,不需要额外“传递”——关键在于选对异常类型、构造方式和抛出时机。
用 getMessage() 和 getCause() 读取已有错误信息
所有 Throwable 子类都自带 getMessage()(返回构造时传入的字符串)和 getCause()(返回嵌套异常)。调用栈里每层只需直接使用,无需手动转发:
- 不要在 catch 块里重新 new 一个异常再塞进 message,比如
new RuntimeException("failed: " + e.getMessage())—— 这会丢失原始堆栈和 cause - 如果要保留原始异常,必须用带
cause参数的构造函数:throw new ServiceException("DB operation failed", e) -
e.toString()会同时输出类名 + message,但不包含 cause;e.printStackTrace()才会展开完整链,日志中建议用logger.error("xxx", e)而非logger.error("xxx " + e)
自定义异常时,只重写构造函数,别动 getMessage()
继承 Exception 或 RuntimeException 后,只要提供标准构造函数即可,JVM 自动处理 message 存储:
public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message); // ✅ 正确:message 自动存入
}
public ValidationException(String message, Throwable cause) {
super(message, cause); // ✅ 正确:cause 和 message 都保留
}
// ❌ 不要重写 getMessage() 返回拼接字符串——干扰调试、掩盖原始意图
}自定义字段(如错误码 errorCode)应单独提供 getter,而不是塞进 message 里。
立即学习“Java免费学习笔记(深入)”;
避免在 finally 或异步线程中吞掉异常
异常传播中断的典型场景不是“不会传”,而是被意外拦截:
-
try { ... } catch (Exception e) { /* 空 */ }—— 错误信息彻底丢失 -
try { ... } finally { close(); }中close()抛异常,会覆盖 try 块里的原始异常(Java 7+ 的 try-with-resources 会自动把 suppressed 异常加到主异常上,可用e.getSuppressed()查看) - 提交到线程池的任务里抛异常,若未显式捕获并处理,该异常只会打印到 stderr,不会传播给调用方
异常传播机制本身很可靠,真正容易出问题的是人为打断链路——比如多层包装却不传 cause、日志只记 message 不打堆栈、或在资源清理逻辑里掩盖了原始失败原因。










