子类重写方法时,throws声明的检查型异常只能是父类对应异常的子类或不声明,运行时异常不受限;接口实现同理,需遵守相同约束。

重写(Override)方法时,子类方法抛出的异常不能比父类方法更宽泛,这是Java语言规范强制要求的,目的是保证多态调用的安全性。简单说:子类方法只能抛出父类方法声明异常的子类型,或不抛出异常,但不能新增检查型异常(checked exception),也不能将父类的检查型异常替换为更上层的异常。
异常范围必须缩小或保持不变
子类重写方法声明的 throws 子句中,所列的检查型异常必须是父类对应方法 throws 中异常的子类,或完全不声明 throws(即不抛出任何检查型异常)。运行时异常(RuntimeException 及其子类)不受此限制,可以自由添加、删除或替换。
- ✅ 允许:父类 throws IOException,子类 throws FileNotFoundException(IOException 的子类)
- ✅ 允许:父类 throws IOException,子类不写 throws(即不抛出检查型异常)
- ❌ 不允许:父类 throws IOException,子类 throws Exception 或 Throwable
- ❌ 不允许:父类未声明 throws 任何检查型异常,子类却 throws SQLException
运行时异常可自由处理
因为 RuntimeException 及其子类属于非检查型异常,编译器不强制捕获或声明,所以重写时可以增加、减少甚至完全改变运行时异常的声明,不影响编译和多态行为。
- 父类方法没写 throws,子类可写 throws IllegalArgumentException
- 父类 throws RuntimeException,子类可改为 throws NullPointerException
- 子类也可在方法体内直接 throw new SecurityException(),无需在 throws 中声明
接口实现时同样适用该规则
类实现接口方法时,也视为一种隐式重写,需遵守相同异常约束。若接口方法没有声明 throws,实现类不得声明检查型异常;若接口声明 throws CloneNotSupportedException,实现类可声明 throws InterruptedException(不行!除非它是 CloneNotSupportedException 的子类——但它不是),所以实际中通常只能缩小或省略。
- 接口方法:void parse() throws ParseException
- ✅ 实现类:public void parse() throws DateTimeParseException(ParseException 的子类)
- ✅ 实现类:public void parse() { ... }(不抛检查型异常)
- ❌ 实现类:public void parse() throws IOException(无关且更宽泛)
常见绕过方式与合理实践
当需要在子类中处理原本父类未覆盖的异常场景时,不应强行扩大 throws 范围,而应考虑封装或转换:
- 将检查型异常转为运行时异常,如 throw new RuntimeException("IO failed", e)
- 在子类方法内部捕获并处理,不向外暴露新异常
- 重构父类方法,提前在抽象层声明更通用的异常(需兼顾所有子类需求)
- 使用 Optional 或特定返回类型替代异常传递错误信息(适用于部分场景)








