printStackTrace() 输出按“从最新到最旧”倒序排列:首行是异常抛出点,末行是入口方法(如main),中间为ClassName.methodName(ClassName.java:lineNumber);Caused by优先分析,Suppressed见于try-with-resources多异常,Native Method表示进入JVM底层。

看懂 printStackTrace() 输出的每一行含义
Java 异常栈迹不是乱码,而是按「从最新到最旧」倒序排列的调用链。第一行是抛出异常的位置(throw 或 JVM 自动抛出点),最后一行通常是入口(如 main 或 run 方法)。中间每行格式为:ClassName.methodName(ClassName.java:lineNumber)。
常见误读:把第一行当成“问题源头”——其实它只是“爆炸点”,真正的问题可能在上层传入了非法参数,或某次 parseDouble("") 返回了 NaN 导致后续计算失败。
- 如果看到
Caused by:块,说明这是被封装的底层异常,优先看它(比如SQLException被包装成RuntimeException) -
Suppressed:表示 try-with-resources 中有多个关闭异常,主异常之外还发生了其他异常 - 行号为
Native Method时,说明调用进入了 JVM 底层(如Object.wait()),无法直接定位 Java 源码
用 getStackTrace() + 日志做结构化异常捕获
直接 e.printStackTrace() 只输出到控制台,不利于线上排查。应转为字符串记录,并补充上下文变量。
String stackTrace = Arrays.stream(e.getStackTrace())
.map(ste -> ste.toString())
.collect(Collectors.joining("\n"));
logger.error("订单处理失败,orderNo={}, userId={}", orderNo, userId, e);
// 注意:e 作为最后一个参数传入,SLF4J 才会自动附加 stackTrace
关键点:
立即学习“Java免费学习笔记(深入)”;
- 不要用
e.toString()替代完整栈迹——它只包含异常类型和 message,丢失调用链 - 避免在日志中拼接
e.getMessage()再手动加栈迹,易漏掉Caused by - 若使用 Logback,可配置
%ex{full}在 pattern 中自动展开完整异常信息
区分 Exception 和 Error 的调试策略
Exception(尤其是 RuntimeException 子类)是你该修复的逻辑问题;Error(如 OutOfMemoryError、NoClassDefFoundError)通常意味着环境或 JVM 状态异常,不应 try-catch 处理,而应查配置或资源。
-
NullPointerException:90% 情况是某个对象未初始化就调用了方法,用 IDE 的「Evaluate Expression」在断点处检查变量是否为null -
ConcurrentModificationException:说明在遍历集合时被其他线程修改,改用CopyOnWriteArrayList或加锁,而不是忽略异常 -
NoClassDefFoundError:不是类没找到,而是类在初始化阶段失败过(比如 static 块抛异常),查java -verbose:class或启动时的早期日志
用 Thread.currentThread().getStackTrace() 主动埋点
当异常不一定会发生,但你想知道某段逻辑「实际被执行到了哪一层」,可用主动堆栈采样代替被动等异常。
public void processRequest(String id) {
if (LOG.isDebugEnabled()) {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
// 跳过当前方法和 getStackTrace 调用本身
LOG.debug("Entered at {}:{}",
trace[2].getClassName(), trace[2].getLineNumber());
}
// ...业务逻辑
}
注意:
- 这个数组开销小但非零,生产环境慎用高频路径
- 返回的数组顺序是「从外到内」(与异常栈相反),
trace[0]是getStackTrace()自身调用 - 比单纯打日志多出 2~3 行代码,但能快速确认是否进入分支、是否被代理拦截、AOP 是否生效
try-catch 只是把错误吞掉,反而让问题更难复现。










