根本区别在于责任归属:checked异常必须由调用方显式处理,因继承exception且非runtimeexception子类,如ioexception;runtimeexception可不处理但反映代码缺陷,因继承runtimeexception,如nullpointerexception。

Java里Checked异常和RuntimeException的根本区别,不在“什么时候抛”,而在“谁该负责处理”。Checked异常是编译器盯住你不放的那类问题,必须显式捕获或声明;RuntimeException则是编译器放手不管、但一出就崩的逻辑漏洞。
核心区别:继承关系决定编译器态度
所有异常都继承自Throwable,但走向完全不同:
-
Checked异常:直接继承Exception,且不是RuntimeException的子类。比如
IOException、SQLException、ClassNotFoundException。编译器强制你用try-catch包住,或在方法签名加throws——不写就报错。 -
RuntimeException:继承自RuntimeException(而
RuntimeException本身又继承Exception)。比如NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException。编译器完全不干预,爱捕不捕,但运行时炸了就得自己兜着。
注意:名字不重要,继承链才作数。哪怕你定义一个MyBusinessException extends Exception,它就是Checked;而MyParamException extends RuntimeException,哪怕叫“Exception”,也是Runtime类型。
设计意图:外部不确定性 vs 内部可预防性
Java把异常分两类,其实是把责任划清楚了:
- Checked异常对应“环境不可控”:文件可能被删、网络可能断、数据库连接可能超时。这些不是你代码写错了,而是现实世界不配合。所以Java要求你提前想好应对策略——重试?换路径?提示用户?
-
RuntimeException对应“代码写错了”:没判空就调方法、数组下标硬写100、类型强转前不检查。这类问题本该在开发阶段就被发现和修复,而不是靠层层
catch掩盖。捕获NPE不如写if (obj != null)来得干净。
简单说:Checked是你得“接住”的意外;Runtime是你该“避免”的失误。
使用选择:什么时候该抛,什么时候该防
选哪一类,关键看你想传达什么信号、希望谁来响应:
-
用Checked异常:当错误发生后,调用方有合理手段恢复——比如读配置失败,可以加载默认值;连接数据库失败,可以切到备用库。这时抛
IOException或自定义ConfigLoadException extends Exception,逼调用方做决策。 -
用RuntimeException:当错误表明调用方用法不对,应该立刻停止并修正——比如传了
null进不允许为空的参数,抛IllegalArgumentException;状态非法时抛IllegalStateException。这类异常不该被静默吞掉,而应让问题暴露在测试或上线初期。 -
慎用全局捕获RuntimeException:在Spring里用
@ControllerAdvice统一处理没问题,但在业务层随便catch (RuntimeException e)再吃掉,容易让空指针藏得更深,调试时更难定位源头。
实践建议:少写冗余catch,多做前置校验
很多新手一见编译报错就本能加try-catch,结果满屏都是防御性代码。其实更优解常在别处:
- 对
FileReader这种明确可能失败的操作,优先考虑是否真需要同步阻塞读——换成异步+回调,或封装成返回Optional<string></string>的方法,语义更清晰。 - 对
list.get(i),与其包一层try-catch ArrayIndexOutOfBoundsException,不如先if (i >= 0 && i ,或者用<code>list.stream().skip(i).findFirst()这类更安全的API。 - 自定义异常时,如果希望强制调用方处理(如“库存不足”需触发补偿流程),继承
Exception;如果只是提醒“参数格式错”,继承RuntimeException更轻量。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











