Java异常分三类:检查型异常(如IOException)、非检查型异常(如NullPointerException)、错误(如OutOfMemoryError);编译期报错多为检查型异常,必须处理;运行时报错且堆栈含“at”的多为非检查型异常,应从堆栈首个项目包路径行定位源头;崩溃类错误需立即排查JVM。

明确异常类型,快速缩小范围
Java异常分三类:检查型异常(如IOException)、非检查型异常(如NullPointerException)、错误(如OutOfMemoryError)。编译期报错的通常是检查型异常,必须处理;运行时报错且堆栈里带“at”行号的,多为非检查型异常,重点看调用链最上层那几行;而直接崩溃、无明显业务代码参与的,比如StackOverflowError或OutOfMemoryError,要立刻转向JVM层面排查。
看懂堆栈信息,定位第一现场
异常打印的堆栈不是从头读,而是从下往上找第一个属于你项目包路径的行。例如:
Caused by: java.lang.NullPointerException
at com.example.service.UserService.updateUser(UserService.java:42)
at com.example.controller.UserController.modify(UserController.java:28)
第42行就是问题源头——不必先猜是不是空值,直接打开UserService.java第42行,看哪一对象没判空就调用了方法。常见操作包括:
- 检查方法参数是否为null(尤其来自前端或RPC调用)
- 查看集合或数组是否已初始化再访问元素
- 确认数据库查询结果是否可能为null,未做判空就直接get()
结合日志与上下文还原执行路径
单靠异常堆栈不够,得看它前面几条INFO/WARN日志。比如ArrayIndexOutOfBoundsException: 163出现前,日志里有“开始批量处理1000条订单”,说明循环索引超了——很可能用了固定size创建ArrayList但没考虑并发扩容,或for循环边界写成而非。建议:
- 在关键入口和分支打结构化日志(含traceId、参数摘要)
- 开启Spring Boot的logging.level.org.springframework.web=DEBUG看请求完整流转
- 对高频异常点加条件断点(如if (list == null || list.size() == 0))
区分环境,避免调试干扰生产
有些异常只在特定环境触发,比如:
- ClassNotFoundException:本地跑得好,上线报错 → 检查Maven依赖树是否有版本冲突,或fat jar未打包某模块
- IllegalStateException:测试环境正常,压测时频发 → 很可能是线程安全问题,如单例Bean里用了非线程安全的SimpleDateFormat
- for debugging purposes only这类提示 → 搜索整个工程是否引入了调试专用异常类,或某些测试框架在prod profile未关闭调试开关










