非受检异常本质是可预防的编程错误,应优先通过源头校验(如objects.requirenonnull)和静态检查预防,而非捕获;仅在明确成因且降级安全时才有限捕获,业务层随意catch runtimeexception是危险信号。

非受检异常可以捕获,但绝大多数情况下不该捕获——它不是运行时的“意外”,而是代码逻辑的“红灯”。Java 设计它的本意不是让你在生产环境里 try-catch,而是推动你在写代码时就把它拦住。
它本质是可预防的编程错误
NullPointerException、IllegalArgumentException、ArrayIndexOutOfBoundsException 这些都不是外部干扰,而是内部可控环节出了疏漏:
- 调用 null 对象的方法 → 源头没校验参数或未初始化依赖
- 数组索引越界 → 循环条件或长度判断有误
- 传入非法年龄值 → 方法入口缺少范围断言(如 Objects.requireNonNull() 或 Assert.state())
编译器不强制捕获,是在提醒:别绕开问题,去修代码本身。
哪些场景才值得捕获?
捕获非受检异常不是常规操作,仅适用于明确知道异常成因、且降级行为安全可控的少数情况:
- 调用第三方 SDK 时它内部抛出 IllegalArgumentException,而你已知该输入属于边缘但可接受情形,可转为默认值返回
- IoT 字典服务中缓存未加载(NullPointerException),切换至内置静态映射表继续响应,而非中断请求
- 协议解析时索引越界(ArrayIndexOutOfBoundsException),降级为返回通用标识 “unknown_device”
这些都不是“兜底”,而是有明确定义、无副作用、可观测的韧性策略。
业务层随意 catch 是危险信号
在 service 方法里写 catch (RuntimeException e),往往意味着:
- 掩盖真实缺陷,让空指针在深层才爆发,堆栈变长、定位变难
- 破坏 Spring 默认事务边界(除非显式配置 rollbackFor)
- 把控制流交给异常机制,带来性能损耗和语义混乱
更稳妥的做法是:用自定义 RuntimeException 表达业务拒绝(如 InsufficientBalanceException),再由全局处理器(@RestControllerAdvice)统一封装返回,既清晰又不侵入业务逻辑。
真正有效的应对方式是预防
与其等异常发生再捕获,不如在源头建立防御习惯:
- 方法入口用 Objects.requireNonNull(param, "userId 不能为空")
- 集合操作前加 if (list != null && !list.isEmpty())
- 字符串转数字前先 StringUtils.isNumeric(str)
- 配合 IDE 的 @NotNull/@Nullable 注解做静态检查
这类主动校验让错误“失败得早、失败得轻”,比任何 catch 块都更可靠。











