受检异常继承Exception但不属RuntimeException体系,编译器强制处理;非受检异常继承RuntimeException或Error,编译器不强制。

怎么一眼判断一个异常是受检还是非受检
看它继承谁:RuntimeException 或 Error 的子类 → 非受检;其他 Exception 子类(且不属 RuntimeException 体系)→ 受检。比如 IOException 继承 Exception,没碰 RuntimeException,编译器就拦你;而 NullPointerException 是 RuntimeException 的直系后代,写完直接能编译通过。
为什么编译器只强制处理受检异常
这不是 JVM 的硬性要求,而是 Java 语言层面的设计约定——编译器(javac)在语法检查阶段主动介入,对非 RuntimeException/非 Error 的 Exception 子类做“必须处理”校验。JVM 运行时其实根本不区分这两类,字节码里抛异常的指令都一样。
- 你漏处理
SQLException?javac报错:unreported exception SQLException; must be caught or declared to be thrown - 你漏处理
IllegalArgumentException?编译器沉默,运行到那儿才崩 - 这个机制本质是“提醒你:这事外部可能出问题,别假装看不见”
实际写代码时最容易踩的坑
把受检异常塞进 lambda、Stream 或 CompletableFuture 里,结果编译不过——因为这些 API 的函数式接口(如 Function、Supplier)方法签名没声明抛出受检异常。
- 错误写法:
list.stream().map(s -> new FileInputStream(s))→FileInputStream构造会抛FileNotFoundException(受检),但Function不允许 - 正确解法:要么用工具类包装成非受检异常(如
throw new RuntimeException(e)),要么改用支持受检异常的第三方接口(如ThrowingFunction) - 更隐蔽的坑:在 Spring 的
@Transactional方法里吞掉受检异常却不抛出,事务可能不会按预期回滚(因为 Spring 默认只对RuntimeException和Error回滚)
自定义异常该选哪一类
取决于调用方有没有能力/责任去恢复它:
立即学习“Java免费学习笔记(深入)”;
- 想强制调用方处理(比如“支付失败需重试或提示用户”)→ 继承
Exception(受检) - 只是参数错了、状态非法、逻辑断言失败 → 继承
RuntimeException(非受检),比如InvalidOrderStatusException extends IllegalArgumentException - 千万别为了“省事”把本该受检的业务异常改成非受检——这等于把可恢复的外部问题,伪装成不可救的程序 bug
真正难的不是分类,而是判断“这个异常,到底该由谁来负责兜底”。很多团队的异常混乱,根源不在语法,而在职责没划清。










