运行时异常指RuntimeException及其子类,编译期不强制处理,反映逻辑错误;受检异常是Exception的其他子类,必须声明或捕获;Error不属于异常,不应捕获。

运行时异常就是 RuntimeException 及其子类
Java 中所有异常都继承自 Throwable,而运行时异常特指 RuntimeException 类及其直接或间接子类,比如 NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException。这类异常在编译期不强制要求处理——你写 list.get(100) 即使 list 只有 2 个元素,编译器也放行。
它们通常反映程序逻辑错误,比如空指针、越界、类型转换失败,应该靠代码审查和测试提前暴露,而不是靠 try-catch 到处兜底。
- 常见误区:以为
throw new RuntimeException("xxx")是“随便抛”,其实它和NullPointerException一样属于运行时异常,调用方完全可不捕获 - 设计原则:自定义业务异常如果不想强制调用方处理,就继承
RuntimeException - 注意:
RuntimeException本身是Exception的子类,但它被 JVM 特殊对待——编译器跳过检查
受检异常必须显式声明或捕获
除 RuntimeException 及其子类外,所有继承自 Exception 但不继承自 RuntimeException 的异常,都是受检异常(checked exception),典型如 IOException、SQLException、ClassNotFoundException。
编译器会强制你面对它们:要么在方法签名中用 throws 声明,要么用 try-catch 包裹。否则编译直接报错:Unhandled exception type XXX。
立即学习“Java免费学习笔记(深入)”;
- 关键判断依据:看异常类是否在
java.lang包里且是RuntimeException的后代;不在java.lang或不是其后代,基本就是受检异常 - 常见陷阱:误把
InterruptedException当成可忽略的运行时异常——它其实是受检异常,必须处理,否则线程中断信号会被静默吞掉 - 性能无关:是否受检和性能无关,只和编译期约束有关;JVM 运行时对两类异常的处理机制完全一致
Throwable 的完整继承结构不能只记两层
很多人只记“Exception 分运行时/受检”,但忽略了 Error 也是 Throwable 的直系子类。像 OutOfMemoryError、StackOverflowError 都属于 Error,它们既不是运行时异常,也不是受检异常——编译器同样不检查,但原则上不应被捕获或处理。
Throwable ├── Error // 不该捕获,如 OutOfMemoryError ├── Exception │ ├── RuntimeException // 运行时异常,不强制处理 │ └── 其他 Exception // 受检异常,必须声明或捕获
- 错误做法:写
catch (Exception e)试图兜住一切,结果连OutOfMemoryError都被拦下,掩盖了真正的问题根源 - 正确姿势:按需捕获具体异常类型,比如只
catch (IOException e),避免宽泛捕获 - 注意:
Exception和Error都继承自Throwable,所以catch (Throwable t)理论上能抓到所有,但生产环境严禁这么写
自定义异常时选错父类会导致调用方困惑
如果你写了一个 InvalidOrderException,却让它继承 Exception,那所有调用它的方法都得加 throws 或 try-catch;如果它继承 RuntimeException,调用方就完全自由。这个选择不是技术问题,而是 API 设计契约。
- 业务异常一般选
RuntimeException:比如参数校验失败、状态不合法,属于调用方应修正的逻辑问题 - 外部依赖异常倾向受检:比如调用支付接口返回 HTTP 500,你封装成
PaymentNetworkException extends Exception,提醒调用方考虑重试或降级 - 容易忽略的点:IDE 自动生成
throws时可能把本该是运行时的异常也加进去,要手动删掉;反之,如果漏了throws导致编译失败,别急着改成运行时异常,先确认是否真该由调用方响应










