核心差异在于责任归属与干预时机:受检异常编译期强制处理(必须try-catch或throws),代表外部不可控但可恢复的问题;运行时异常编译期不检查,反映代码逻辑缺陷,应通过预防而非捕获来解决。

处理受检异常和运行时异常,核心差异不在“要不要捕获”,而在于“谁该负责、何时干预、怎么修复”。
受检异常:必须显式应对,重点在恢复与决策
编译器强制你面对——不 try-catch,也不 throws,代码根本编译不过。这不是限制,而是提醒:这类异常代表程序**无法控制但大概率会发生**的外部状况。
-
典型场景:文件读写失败(
IOException)、数据库连接中断(SQLException)、类加载找不到(ClassNotFoundException) - 正确策略:不是简单吞掉,而是做有意义的响应——重试、降级、提示用户、切换备用路径
-
注意陷阱:
InterruptedException是受检异常,不能忽略;捕获后应调用Thread.currentThread().interrupt()恢复中断状态,否则上层永远收不到停止信号
运行时异常:默认不强制处理,重点在预防与根因修复
编译器放行,不代表可以不管。它们几乎都指向代码本身的缺陷,比如空值、越界、非法参数。靠 catch 来兜底,往往只是掩盖问题。
-
典型场景:
NullPointerException(未判空)、ArrayIndexOutOfBoundsException(下标错)、IllegalArgumentException(参数校验缺失) - 正确策略:在源头拦截——加 null 检查、范围校验、使用 Optional、改进 API 设计;捕获仅用于兜底日志或统一错误页,不建议静默吞掉
-
避免反模式:不要用
try { obj.doSomething() } catch (NullPointerException e) { },这等于主动放弃调试线索
自定义异常:按语义选父类,不为省事妥协
继承哪个基类,本质是向调用方传递设计意图。
立即学习“Java免费学习笔记(深入)”;
-
继承
Exception:适用于业务上可预期、调用方理应主动决策的失败,比如“余额不足”“权限不足”“库存扣减失败”。强制对方考虑重试、补偿或友好提示 -
继承
RuntimeException:适用于明显是调用方用错了的情况,比如传了 null ID、状态枚举值非法。靠单元测试和 Code Review 暴露,不污染方法签名 - 切忌一刀切:全做成运行时异常 → 错误沉默、线上难排查;全做成受检异常 → 每层都 throws,API 膨胀、调用成本高
通用避坑原则
两类异常共用的底线规则,容易被忽视但影响深远:
-
别写
catch (Exception e):它会拦下OutOfMemoryError这类 JVM 级灾难,导致系统带病运行、问题恶化 -
别捕获
Error及其子类:如StackOverflowError、NoClassDefFoundError,它们不是程序能恢复的状态 -
异常信息要保留上下文:抛出时用带参构造,捕获时至少打印堆栈;不要只写
log.error("出错了")


















