子类重写父类方法时,抛出的检查型异常不得比父类更宽:只能省略或缩小(子类异常),禁止扩大(父类异常的父类或无关类型);运行时异常不受限;多态调用按父类throws声明捕获。

子类重写父类方法时,抛出的检查型异常(checked exception)不能比父类更宽——这是编译器强制执行的契约规则,不是建议,而是必须遵守的语法底线。
核心约束:只许缩小,不许扩大
所谓“范围”,指的是异常类型的继承层级。父类方法声明 throws IOException,子类重写时:
- ✅ 可以不写 throws(即完全不抛出检查型异常)
- ✅ 可以 throws FileNotFoundException(它是 IOException 的子类,更具体)
- ❌ 不能 throws Exception(比 IOException 更宽泛)
- ❌ 不能 throws SQLException(与 IOException 无继承关系,属于平行类型)
- ❌ 父类没写 throws → 子类绝不能新增 throws IOException 或任何检查型异常
运行时异常完全自由
RuntimeException 及其子类(如 IllegalArgumentException、NullPointerException、自定义的 ValidationException extends RuntimeException)不受该规则限制:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 父类方法体里没 throw 过任何异常,子类可以 throw new IllegalArgumentException("参数为空")
- 父类 throws IOException,子类在方法内部额外 throw new NumberFormatException(),合法且无需声明
- 这种自由源于设计定位:运行时异常代表程序逻辑缺陷或不可恢复错误,本就不该由调用方强制兜底
多态调用时,捕获逻辑只看父类声明
用父类引用调用重写方法,编译器只认父类方法签名中的 throws —— 这是安全前提:
- Animal a = new Dog(); try { a.speak(); } catch (IOException e) { ... }
- 即使 Dog.speak() 实际抛出的是 FileNotFoundException,你也只能按 IOException 写 catch
- 不能写 catch (FileNotFoundException e),因为编译器不知道运行时是哪个子类,只信任父类契约
实际应对策略要分场景选
当子类逻辑确实需要暴露父类未声明的新错误时,有几种合规解法:
- 把新异常包装成 RuntimeException:throw new RuntimeException("DB 访问失败", sqlEx)
- 在子类内部 try-catch 消化掉额外异常,转为返回 Optional.empty()、错误码或 Result
对象 - 若架构允许,提升父类 throws 声明(如改为 throws Exception),但需谨慎——这会加重所有调用方的处理负担
- 异常语义差异大时,考虑用组合替代继承,把易变的异常逻辑抽离为策略组件
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










