上周帮同事看一个定时任务突然不执行的问题,本地跑得好好的,一上测试环境就卡住。最后发现是某处用了 System.out.println 打印日志,结果被日志框架拦截后没输出,反而掩盖了空指针异常——这种场景,光靠“加打印”早就不够用了。
别再只靠 System.out.println
很多老项目里还留着满屏的 System.out.println("user: " + user),看着像在调试,实则效率低、难定位、上线还得手动删。IDE 自带的调试器才是主力,比如 IntelliJ 或 Eclipse 的断点调试,比手写日志快十倍。
基础断点:不只是“停一下”
在代码行号左侧单击就能加断点,但很多人不知道右键可以设置条件:
if (userId == 10086) { // 只有这个用户触发时才停下
processOrder(order);
}
这样不用反复点“Resume”,也不用改代码重跑,特别适合排查偶发问题。
变量实时看,比 log 更准
断点停住后,直接看 Debugger 窗口里的 Variables 面板,所有局部变量、this 对象属性、甚至集合里的元素都能展开查看。遇到 Map 里 key 是自定义对象又没重写 toString()?右键选 “View as Object” 就能一层层点开字段值,比翻 log 文件快多了。
远程调试:测试环境也能“连上去”
本地复现不了?试试远程调试。启动 Java 进程时加上参数:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
然后在 IDEA 里配置 Remote JVM Debug,填上服务器 IP 和端口,点 Debug 就连上了。注意防火墙和云服务器安全组要放行 5005 端口。
日志不是对手,是搭档
有些场景不适合打断点:高频调用接口、多线程竞争、或者生产环境不敢轻易挂起。这时候得靠日志配合。
推荐用 SLF4J + Logback,配合 MDC 做请求级追踪:
MDC.put("traceId", UUID.randomUUID().toString());
log.info("订单创建开始");
// ...业务逻辑
log.info("订单创建完成,金额:{}", order.getAmount());
MDC.clear();
日志里自动带上 traceId,查问题时 grep 一把全链路日志就齐了。
还有几个小技巧很管用
- 在 IDEA 里按 Ctrl+Shift+F7(Windows)或 Cmd+Shift+F7(Mac),能高亮当前变量所有引用位置;
- 异常抛出时,直接在 Debug 模式下打开 “Breakpoints” 设置面板,勾上 “Java Exception Breakpoints”,选中
NullPointerException,一出现就停住; - 想快速验证某段逻辑?选中代码 → 右键 → “Evaluate Expression”,支持写表达式、调方法、甚至新建临时对象。
调试不是玄学,也不是堆时间。用对工具,一次远程断点可能比改十次日志更快找到根因。