受检异常必须显式处理(try-catch或throws),否则编译失败,适用于可恢复的外部问题如IO异常;非受检异常(RuntimeException/Error及其子类)编译期不强制处理,反映编程错误或系统级故障,应优先修正而非捕获。

区分受检异常与非受检异常的处理策略,关键看两点:编译器是否强制你处理,以及异常反映的问题性质。受检异常必须显式处理,否则编译失败;非受检异常可不处理,但建议按需捕获用于调试或降级。
看继承关系和类归属
受检异常是 Exception 的子类,但不包括 RuntimeException 及其子类。典型例子有 IOException、SQLException、ClassNotFoundException。它们在编译期就被检查,未处理就报错。
非受检异常包含两类:
- RuntimeException 及其子类:如 NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException;
- Error 及其子类:如 OutOfMemoryError、StackOverflowError。
这两类都不需要在方法签名中声明,也不强制 try-catch。
看编译阶段是否报错
写代码时如果抛出受检异常,编译器会直接标红并提示“Unhandled exception type XXX”:
- 必须用 try-catch 捕获,并在 catch 块中做有意义的处理(比如重试、记录日志、返回友好提示);
- 或者用 throws 声明向上抛出,把责任交给调用方;
- 二者选其一,缺一不可。
而非受检异常不会导致编译失败。即使你写了 throw new NullPointerException(),代码也能通过编译——但它一旦运行时发生,又没被 catch,就会中断当前线程并打印堆栈。
看问题来源和修复方式
受检异常通常对应外部可恢复的意外情况,比如文件不存在、网络超时、数据库连接失败。这类问题不是代码写错了,而是环境或资源出了状况,程序有能力感知并响应。
非受检异常往往暴露程序逻辑缺陷,比如访问 null 对象、数组下标越界、除零。它们该在开发和测试阶段被发现并修正,而不是靠运行时捕获来兜底。
- 对 RuntimeException,更推荐用防御性编程提前规避(例如判空、校验参数范围);
- 必要时才捕获,比如统一异常处理器中记录日志、转换为用户友好的错误码;
- Error 类一般不捕获,因为 JVM 已处于不稳定状态,强行处理意义不大。
看实际编码中的写法差异
以读取文件为例:
-
受检异常写法:
FileReader fr = new FileReader("a.txt");→ 编译报错,必须加 try-catch 或 throws IOException; -
非受检异常写法:
String s = null; s.length();→ 编译通过,运行时报 NullPointerException;
这种语法层面的强制性,就是最直观的区分依据。

















