子类重写父类方法时,检查异常范围不能变宽:可保持、缩小或消除父类声明的检查异常,但不可扩大;运行时异常完全自由;父类未声明检查异常时子类不得新增;设计上推荐用工具方法、策略模式或Result封装替代继承式异常处理。

子类重写父类方法时抛出异常,核心是守住“异常范围不能变宽”这条线。不是自由发挥,而是严格遵循编译器强制的契约一致性规则——调用方按父类签名写代码,运行时绝不能突然冒出它没准备处理的新检查异常。
只允许缩小或去掉检查异常
父类声明 throws IOException,子类可以:
- 保持原样:throws IOException
- 换成更具体的子类:throws FileNotFoundException(它是 IOException 的子类)
- 干脆不抛:方法体里用 try-catch 消化掉,不写 throws
不能写 throws Exception、throws SQLException 或 throws Throwable——这些要么是父类异常的父类,要么与父类异常无关,编译直接报错:“overridden method does not throw …”
运行时异常完全自由
RuntimeException 及其子类(如 IllegalArgumentException、NullPointerException、DataAccessException)不受限制:
- 父类没声明任何异常,子类仍可 throw new IllegalArgumentException("invalid input")
- 父类已声明 throws IOException,子类还能额外在逻辑中 throw new RuntimeException(e)
这类异常不强制调用方捕获,不影响多态调用的安全性,是处理“子类特有失败场景”的常用方式。
父类没声明检查异常,子类也不能加
如果父类方法是 void doWork()(无 throws),子类重写时:
- 禁止写 throws IOException、throws SQLException 等——编译失败
- 若内部调用了可能抛检查异常的方法(如 new FileInputStream()),必须自己 try-catch
- 推荐做法:捕获后包装成运行时异常再抛,例如 throw new DataAccessException("read failed", e)
设计层面更稳妥的选择
当多个子类异常语义差异大,硬套继承容易让 throws 声明越来越臃肿:
- 把共用逻辑抽成工具方法,由各子类自行调用并独立处理异常
- 用策略模式把异常处理行为参数化,避免异常类型绑定在继承链上
- 返回 Result
或 Optional 而非抛异常,把错误控制权交给调用方
不复杂但容易忽略——关键在分清:检查异常是对外契约,运行时异常是内部问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











