printStackTrace()可快速定位异常源头,但生产环境应避免裸调用;获取结构化异常信息需用getMessage()、getCause()、getClass().getName()等方法;转堆栈为字符串推荐StringWriter+PrintWriter;自定义fillInStackTrace()需谨慎防止getStackTrace()返回null。

用 printStackTrace() 快速定位异常源头
开发时遇到未捕获异常,最直接的方式是让 JVM 打印完整调用栈。这能立刻看到异常类型、消息和每层方法调用位置。
注意:printStackTrace() 默认输出到 System.err,不是 System.out,别在日志里只盯 stdout 而漏看它。
- 在
catch块中调用e.printStackTrace()是最快捷的调试手段 - 如果运行在容器或日志系统中,建议改用
e.printStackTrace(new PrintWriter(stringWriter))捕获为字符串再交由日志框架处理 - 不要在生产代码中长期保留裸调用
printStackTrace(),它绕过日志级别控制且不易检索
提取异常核心信息:message、cause、class name
日志记录或 API 返回错误时,通常只需要结构化字段,而非整段堆栈。Java 异常对象本身提供稳定接口获取关键元数据。
getMessage() 只返回构造时传入的字符串,可能为空;getCause() 返回原始异常(如 SQLException 包裹的 IOException),需递归获取才完整;getClass().getName() 比 toString() 更可靠,避免被子类重写 toString 导致格式混乱。
立即学习“Java免费学习笔记(深入)”;
-
e.getMessage()—— 不一定有内容,空指针或 IO 异常常返回 null -
e.getCause() != null ? e.getCause().getMessage() : "no cause"—— 需判空,否则 NPE - 嵌套异常建议用
ThrowableUtils.getRootCause(e)(Apache Commons Lang)或手动 while 循环遍历getCause()
获取完整堆栈字符串:避免 printStackTrace() 的副作用
想把堆栈存进数据库、上报监控或拼进 JSON 日志,必须拿到字符串形式。不能依赖 printStackTrace() 的输出流副作用。
StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String stackTrace = sw.toString();
这段代码是标准解法。注意 StringWriter 和 PrintWriter 都是内存对象,无 I/O 开销,但要确保 sw 不被意外复用(比如放在静态变量里)。
- 别用
e.toString()代替——它只含类名+message,没有堆栈行 - JDK 8+ 可用
Arrays.toString(e.getStackTrace()),但格式难读,不推荐用于日志 - Log4j2 / SLF4J 等框架默认会自动展开 cause 和 stack trace,配置好
%throwable即可,无需手动转字符串
自定义异常信息增强:覆盖 fillInStackTrace() 的陷阱
有些场景需要隐藏真实堆栈(如安全限制)、或注入上下文(如请求 ID、用户 ID)。这时会重写 fillInStackTrace(),但极易出错。
常见误操作是直接 return this,导致后续 getStackTrace() 返回空数组;正确做法是保留原逻辑并追加元素,或使用 setStackTrace() 显式设置。
- 若禁用堆栈(如性能敏感中间件),应明确调用
super(null)构造,并在文档中标注“no stack trace” - 向异常注入上下文,优先用字段存储(如
private final String traceId),而非篡改堆栈字符串 - 重写
fillInStackTrace()后,getCause()和getSuppressed()仍正常,但getStackTrace()可能为 null 或长度为 0,所有消费方都得做防御性判断










