java编译器强制检查受检异常,未try-catch或throws则编译失败;非受检异常编译期完全不检查,即使明知道运行时必崩也能通过编译。

Java 编译器对这两类异常的校验方式截然不同:受检异常必须显式处理,否则编译直接失败;非受检异常完全不参与编译检查,代码能顺利通过编译,哪怕明知道运行时必崩。
受检异常:编译器强制拦截
只要代码中调用了可能抛出受检异常的方法(如 new FileInputStream("a.txt") 或 Files.readAllBytes(Paths.get("x"))),而你既没用 try-catch 捕获,也没在方法签名加 throws IOException,javac 就会报错:
- error: unreported exception IOException; must be caught or declared to be thrown
- 这不是警告,是硬性编译失败,无法生成 class 文件
- 常见踩坑点:粘贴含文件操作的代码、在 lambda 里调用受检 API(lambda 无法声明 throws)
非受检异常:编译器全程“视而不见”
像 NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 这些 RuntimeException 子类,以及所有 Error(如 OutOfMemoryError),编译器完全不检查:
- String s = null; s.length(); —— 编译通过,运行时才抛 NPE
- int[] a = {}; a[0]; —— 同样编译无误,运行时报 ArrayIndexOutOfBoundsException
- 即使手动写 throw new RuntimeException("test");,也不会触发任何编译错误
一眼判断是否受检:看继承链
核心规则就一条:继承自 Exception,但**不经过 RuntimeException** 的,就是受检异常。
- ✅ 受检:IOException、SQLException、InterruptedException、ParseException —— 都是 class XxxException extends Exception
- ❌ 非受检:NullPointerException、IllegalArgumentException —— 都是 class XxxException extends RuntimeException
- ❌ 非受检:OutOfMemoryError、StackOverflowError —— 都是 class XxxError extends Error
为什么这样设计:区分“可防”与“该改”
编译器较真对待受检异常,是因为它们代表程序**无法控制但必须响应的外部状况**——比如磁盘坏了、网络断了、数据库连不上。这些不是 bug,是现实风险,得提前写好应对逻辑。
而非受检异常暴露的是**本不该发生的逻辑缺陷**——空指针、越界、类型强转失败。编译器不拦你,是默认你应该靠测试、静态检查、防御性编程来消灭它们,而不是靠 catch 来兜底。











