真正出问题的代码通常在caused by之后、最靠近底部的at行,如userprocessor.java:23;需优先检查该行附近变量是否为null,而非调用入口。

看懂堆栈跟踪(Stack Trace)里的关键行
Java异常抛出时打印的堆栈信息不是从上到下都重要,真正出问题的代码通常在Caused by之后、最靠近底部的at行。比如:
Exception in thread "main" java.lang.NullPointerException
at com.example.UserProcessor.process(UserProcessor.java:23)
at com.example.UserProcessor.main(UserProcessor.java:15)这里第23行才是空指针发生的实际位置,第15行只是调用入口——优先检查UserProcessor.java:23这一行附近的变量是否为null,而不是一上来就翻main方法。
区分Exception和Throwable.getCause()链
很多异常是包装过的,比如SQLException套着RuntimeException,原始错误可能藏在getCause()里。直接打印e只会看到外层异常,容易误判:
- 用
e.printStackTrace()能看到完整嵌套链,但日志中不推荐(影响可读性) - 更稳妥的是循环调用
e = e.getCause(),直到e == null,重点关注第一个非RuntimeException或Exception的底层原因 - Spring项目常见
NestedServletException,它的getRootCause()比getCause()更可靠
在IDE里快速跳转到异常行(以IntelliJ为例)
堆栈里带文件名和行号的at行,IntelliJ默认支持Ctrl + 点击跳转,但有几个前提:
- 确保编译输出的
.class文件包含调试信息(即编译时未加-g:none) - 源码路径要和
ClassNotFoundException报的包路径严格一致(大小写、斜杠方向都不能错) - 如果跳转失败,右键堆栈行 →
Jump to Source,IDE会提示缺失源码,这时需手动关联src目录或添加依赖源码jar
捕获异常时别只打e.toString()
e.toString()只返回异常类名+消息,丢掉了堆栈和嵌套关系,对定位毫无帮助:
- 至少用
e.getMessage()配合e.getStackTrace()手动拼接,或直接用String.valueOf(e)(效果类似printStackTrace()的字符串版) - 生产环境建议用SLF4J:用
logger.error("处理用户失败", e),第二个参数传异常对象,日志框架会自动展开完整堆栈 - 千万别写
catch (Exception e) { System.out.println(e); }——这等于把异常“吃掉”还假装没发生
堆栈里每行at背后都对应一次方法调用,但JVM内联优化可能让实际执行点和显示行号有偏差;如果怀疑是JIT优化导致行号不准,可临时加-XX:-TieredStopAtLevel=1禁用C2编译器验证。
立即学习“Java免费学习笔记(深入)”;










