子类重写父类方法时,声明的检查型异常范围不得宽于父类,否则编译报错;若父类未声明throws,子类不可抛任何检查异常;超出范围时应转为运行时异常、内部捕获处理或谨慎升级父类异常声明。

子类重写父类方法时,如果声明抛出的检查型异常范围比父类更宽,编译器会直接报错,例如:error: overridden method does not throw ... 或 unreported exception ... must be caught or declared。这不是运行时问题,而是编译期强制契约——目的是保障多态调用时异常处理逻辑的安全与可预期。
确认父类 throws 声明的实际范围
先看父类方法签名中的 throws 列表,注意两点:
- 若父类没写
throws,子类就**不能声明任何检查型异常**(如IOException、SQLException) - 若父类写了
throws IOException,子类只能抛IOException及其子类(如FileNotFoundException),不能抛Exception、Throwable,也不能抛无关类型(如SQLException)
把新增的检查异常转为运行时异常
当子类逻辑确实需要处理父类未覆盖的检查异常(比如调用了新IO操作或数据库访问),又无法修改父类签名时,常用做法是包装成 RuntimeException:
- 用
throw new RuntimeException(e)直接抛出 - 或封装为业务相关的运行时异常,如
throw new DataAccessException("DB error", e) - 这类异常不参与编译检查,不会破坏原有异常契约
在子类内部捕获并消化异常
如果该异常属于实现细节,调用方无需感知,就应在子类方法体内处理:
立即学习“Java免费学习笔记(深入)”;
- 用
try-catch捕获检查异常 - 做日志记录、降级处理(如返回默认值)、或转换为父类允许的异常再抛出
- 例如:父类声明
throws DataAccessException,子类遇到IOException,可捕获后throw new DataAccessException("read failed", e)
谨慎调整父类 throws 声明
如果设计上确实需要子类扩展异常能力,且影响可控,可升级父类接口:
- 将父类
throws IOException改为throws Exception——但要评估对所有子类和调用方的影响 - 更推荐的方式是定义一个更通用的自定义检查异常(如
StorageException),让父类声明它,子类再按需抛其子类 - 接口或抽象父类的改动需团队共识,避免“为解一个编译错误而污染整个继承体系”


















