java异常分受检与非受检:继承exception但不经过runtimeexception的(如ioexception、sqlexception)必须处理;继承runtimeexception或error的(如nullpointerexception、illegalargumentexception)无需强制处理,编译器仅提醒可恢复外部问题用受检,逻辑错误用非受检。
java里exception类下面的异常,不是全都要你“必须处理”的。关键看它到底属于哪一支——是runtimeexception的后代,还是纯粹的exception子类但没碰runtime体系。
一眼识别:看继承关系最准
受检异常一定继承自Exception,但不经过RuntimeException;非受检异常只要往上找,能摸到RuntimeException或Error,就是编译器不管的那一类。
- IOException → Exception子类,和RuntimeException没关系 → 受检
- NullPointerException → RuntimeException子类 → 非受检
- SQLException → Exception子类,也不在Runtime分支 → 受检
- IllegalArgumentException → RuntimeException子类 → 非受检
编译器态度不同:一个拦你,一个装看不见
受检异常在编译阶段就会卡住你:没try-catch、也没throws?javac直接报错,比如“unreported exception IOException; must be caught or declared”。而非受检异常哪怕你明写throw new NullPointerException(),也能顺利编译通过。
- 这是语言设计层面的约定,不是JVM运行时的要求
- JVM字节码里抛异常的指令对两者完全一样
- 拦你的其实是javac,目的是提醒:“这事可能出在外头,别假装没事”
该不该强制调用方处理?得看问题性质
受检异常适合那些外部可恢复、调用方有责任应对的情况,比如文件读不到、数据库连不上——这时候强迫写try或throws,其实是帮人想起要加重试、提示用户或记录日志。
- 非受检异常多对应程序逻辑缺陷:参数为空、下标越界、除零——这类问题应该靠代码预防,而不是靠catch兜底
- 自定义业务异常时,如果希望上游必须处理(如支付失败需引导重试),就继承Exception;如果是校验失败、状态非法等内部问题,直接继承RuntimeException更合适
- 别为了省事把本该受检的异常改成RuntimeException——等于把“可救的故障”伪装成“不可救的bug”
实际编码中几个典型坑
这些地方容易栽跟头,不是语法难,而是规则和场景没对上。
- Stream或Lambda里抛受检异常会编译不过,因为Function/Supplier等接口没声明throws,解决办法是包装成RuntimeException,或引入ThrowingFunction
- @Transactional方法里吞掉受检异常却不往外抛,事务可能不会回滚——Spring默认只对RuntimeException和Error触发回滚
- 在API方法签名里堆一堆throws IOException、SQLException,会让调用方负担过重;如果上层根本没法恢复,不如提前转成非受检异常再抛
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











