RuntimeException 是程序员逻辑错误的信号灯,非系统故障,不强制捕获;其子类如 NullPointerException、NumberFormatException 等均对应可预防的编码漏洞,应修源码而非加 try-catch。

RuntimeException 是程序员自己写错的信号灯,不是系统故障——它不强制捕获,因为 JVM 认为:这类异常本不该发生;一旦出现,说明代码逻辑有硬伤,该修的是源码,不是加 try-catch。
为什么编译器不管 RuntimeException?
Java 把异常分为两类:checked exception(如 IOException)必须声明或捕获,而 RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)属于 unchecked。这不是疏忽,是设计取舍:
- 编译时无法预判空指针:你传进来的
String input是不是null,编译器看不到运行时数据流 - 数组越界检查成本太高:每次访问
arr[i]都做边界校验会拖慢所有合法访问,JVM 选择“出错了再报”,而非“每步都防” - 除零、类型转换失败等,本质是参数/输入不符合契约,应由调用方保证,而非靠异常兜底
常见 RuntimeException 背后的真实问题场景
它们不是随机抛出的,每个都对应一类可预防的编码漏洞:
-
NullPointerException:常出现在未判空就调用obj.toString()或map.get(key).length()—— 正确做法是提前用Objects.nonNull()或 Optional 封装 -
NumberFormatException:来自Integer.parseInt("abc")这类强转,但用户输入不可信时,应先用正则或StringUtils.isNumeric()过滤,或改用Integer.valueOf(String)+catch -
ArithmeticException: / by zero:出现在int result = a / b;中b == 0—— 除法前加if (b != 0)比捕获异常更轻量、更直观 -
ClassCastException:比如(String) new Integer(42),多见于泛型擦除后的原始集合操作,应优先用带类型信息的集合(List),避免裸List
什么时候该 catch RuntimeException?
极少情况才需要——不是为了“容错”,而是为了“可控降级”:
立即学习“Java免费学习笔记(深入)”;
- 解析外部不可控输入(如 CSV 行、HTTP query 参数)时,
NumberFormatException可捕获并记录 warn 日志,跳过该条数据,而非让整个批处理中断 - 调用第三方 SDK(内部大量使用
Objects.requireNonNull())时,若其文档明确说明某些参数 null 会抛IllegalArgumentException,且你无法控制上游,可捕获并 fallback - 绝对不要这样做:
try { doSomething(); } catch (RuntimeException e) { /* 吞掉或打印后继续 */ }——这等于把 bug 掩盖成“正常流程”
最易被忽略的一点:RuntimeException 的构造函数支持传入 cause,但很多人直接 throw new RuntimeException("xxx"),丢掉了原始异常栈。真要包装,务必用 new RuntimeException("msg", cause),否则排查时永远卡在“不知道谁抛的”。










