应优先用 runtimeexception 表示逻辑错误(如参数非法、状态不符),受检异常仅用于需调用方显式恢复的外部失败(如网络超时);illegalargumentexception 用于参数校验,illegalstateexception 用于状态校验;自定义异常默认继承 runtimeexception,避免无意义的 throws 声明。

该用 RuntimeException 还是受检异常?
Java 异常选择的核心分歧就在这里:是否强制调用方处理。如果异常代表程序逻辑错误(比如传了 null 给不允许为空的参数),应该用 RuntimeException 子类;如果异常代表外部可恢复的失败(比如文件不存在、网络超时),且你希望调用方明确决策——重试、降级或抛出,那就用受检异常(Exception 的非 RuntimeException 子类)。
常见误用:把数据库连接失败包装成 RuntimeException,导致上层完全忽略重连逻辑;或者把 IllegalArgumentException 声明为受检异常,强迫所有调用点写 try-catch,违背语义。
- 检查方法签名是否已有
throws—— 如果已有多个受检异常,新增一个要谨慎,避免接口污染 - 看异常是否可能被上层捕获并有意义地处理:能重试/切换策略的,倾向受检;只能记录日志后崩溃的,用运行时异常
- Spring 等主流框架默认将多数底层异常(如 JDBC 异常)转为
RuntimeException,不是因为“不该检查”,而是认为数据访问层失败通常不可预期恢复,由统一异常处理器兜底更合理
IllegalArgumentException 和 IllegalStateException 怎么分?
两者都是 RuntimeException,区别在于“错在输入”还是“错在状态”。前者针对方法参数非法(例如传了负数给表示数量的参数),后者针对对象当前状态不满足执行前提(例如调用 iterator.next() 但已到末尾,或对已关闭的流再读取)。
容易混淆的点:看似是参数问题,实则是状态问题。比如向一个只允许添加一次的容器重复添加,抛 IllegalStateException 更准确——不是参数错了,是容器状态已不允许该操作。
立即学习“Java免费学习笔记(深入)”;
-
IllegalArgumentException:校验发生在方法入口,独立于对象生命周期 -
IllegalStateException:校验依赖对象内部状态(如private boolean closed),且该状态可能随时间变化 - 不要为了省事统一用
IllegalArgumentException,IDE 和静态分析工具(如 ErrorProne)会据此做语义检查,用错会掩盖真实问题
自定义异常要不要继承 RuntimeException?
绝大多数情况下要。除非你能明确说出“为什么必须让每个调用方都声明 throws”,否则默认走运行时异常。自定义异常的本质是提供更精确的错误语义和携带上下文信息(比如订单ID、用户ID),而不是制造调用负担。
典型反例:定义一个 OrderValidationException extends Exception,结果所有 service 方法都得加 throws OrderValidationException,而实际业务中它永远被立即捕获并转成 HTTP 400 响应——这种受检设计纯属冗余。
- 如果异常需要被特定层级捕获(如网关层统一拦截认证失败),用运行时异常 + 特定基类(如
AuthException extends RuntimeException),配合 AOP 或全局异常处理器 - 若必须带检查(极少见,如某些金融核心系统要求所有业务异常显式声明),确保有配套的文档和模板代码,否则开发者会绕过(比如直接 throw new RuntimeException(...))
- 自定义异常构造函数务必支持
cause参数,方便保留原始堆栈(new MyException("msg", cause))
为什么 NullPointerException 不该手动 throw?
它专用于 JVM 在解引用 null 时自动抛出,语义是“程序有 bug,变量没初始化或意外置空”。手动 throw NullPointerException 混淆了故障根源:你是想报参数为空(该用 Objects.requireNonNull),还是真遇到了 JVM 层面的空指针?
JDK 7+ 后,Objects.requireNonNull 已成为标准方式。它抛的是 NullPointerException,但语义清晰、堆栈指向调用点而非深层执行路径,且支持自定义消息。
- 禁止:
if (obj == null) throw new NullPointerException(); - 推荐:
Objects.requireNonNull(obj, "obj must not be null"); - 注意
requireNonNull返回非空值,可链式使用:process(Objects.requireNonNull(input)) - 第三方库(如 Guava)的
Preconditions.checkNotNull行为类似,但 JDK 原生已足够










