非受检异常是继承自RuntimeException的异常,如NullPointerException等,本质是可预防的逻辑缺陷而非环境问题,设计上强调预防优于捕获,不应依赖try-catch兜底。

非受检异常(Unchecked Exception)在 Java 中特指继承自 RuntimeException 的异常,如 NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等。它们之所以被设计为“非受检”,根本原因不是因为不重要,而是因为——它们本不该在运行时才暴露,而应在编码阶段就被发现和消除。
为什么说它是逻辑缺陷,而不是环境问题
受检异常(如 IOException)对应的是外部不可控因素:文件可能被删、网络可能断、数据库可能宕机。这些情况再严谨的代码也无法 100% 避免,所以编译器强制你“提前想好怎么办”。
而非受检异常几乎都源于内部可控制的行为:
-
空值误用:调用
null对象的方法或字段 →NullPointerException -
边界疏忽:用负数或超长索引访问数组 →
ArrayIndexOutOfBoundsException -
参数校验缺失:传入非法值却未做判断 →
IllegalArgumentException -
类型误转:强制转换不兼容类型 →
ClassCastException
这些都不是“运气不好”,而是代码逻辑链条中某处漏掉了必要检查或约束。
设计上鼓励“预防优于捕获”
Java 不强制你 try-catch 这类异常,正是为了传递一个明确信号:别靠捕获来兜底,要靠写对代码来避免。例如:
- 用
Objects.requireNonNull()主动抛出清晰的NullPointerException,比让 null 沉默穿透到深层再崩更利于定位 - 在方法入口校验参数范围,早失败、早反馈,而不是等下标越界才报错
- 用 Optional 封装可能为空的返回值,把 “是否为空” 变成显式契约,而非隐式风险
捕获它往往意味着设计妥协
真有必要捕获非受检异常,通常说明两种情况:
- 你在处理第三方库的不规范行为(比如它文档没写会返回 null,但实际会)
- 你在做防御性编程兜底(如解析用户输入时防止整个请求因格式错误而崩溃),但这属于容错策略,不是正常流程
把 catch (NullPointerException e) 当作常规手段,就像给汽车油箱加防盗盖却不修漏油的管子——治标不治本。
它和 Error 的关键区别
有人混淆 RuntimeException 和 Error(如 OutOfMemoryError),但二者定位不同:
-
Error是 JVM 层崩溃,程序无法也不应恢复 -
RuntimeException是你写的逻辑出了偏差,完全可以通过改进代码修复
所以它的“非受检”不是放任,而是信任:Java 相信开发者有能力、也应该有责任,在编译和测试阶段就把这类问题拦住。


















