Java方法重写时检查异常只能缩小或取消,不可扩大;运行时异常可自由添加;推荐捕获检查异常并封装为带cause的业务运行时异常。

Java 方法重写时抛出异常,核心是守住“调用方契约不变”——编译器只认父类方法签名,不看子类实现。只要子类让调用方突然面对更多必须处理的检查异常,编译就直接拒绝。
检查异常必须缩小或去掉
父类方法声明 throws IOException,子类重写时只能:
- 保持原样:throws IOException
- 换成更具体的子类:throws FileNotFoundException(它是 IOException 的子类)
- 完全不抛:方法体里用 try-catch 拦住,不加 throws
- 不能写 throws Exception、throws SQLException(除非它真是 IOException 的子类),否则报错 “overridden method does not throw”
父类没声明检查异常,子类也不能加
如果父类方法是 void doWork(),没有 throws,那子类重写时:
- 不允许写 throws IOException 或任何检查异常,编译失败
- 遇到文件操作、数据库连接等可能抛检查异常的代码,必须自己 try-catch
- 常见做法:捕获后包装成 RuntimeException,例如 throw new RuntimeException("read config failed", e)
运行时异常完全自由
RuntimeException 及其子类(如 IllegalArgumentException、NullPointerException)不受限制:
立即学习“Java免费学习笔记(深入)”;
- 父类没声明异常,子类可以 throws IllegalArgumentException
- 父类已声明 throws IOException,子类还能额外加 throws IllegalArgumentException
- 这类异常不强制调用方处理,不影响多态调用的安全性
推荐做法:封装而非扩大声明
多数场景下,与其在子类里硬加 throws,不如内部消化并封装:
- 用 try-catch 捕获底层检查异常
- 包装成业务语义明确的运行时异常,比如 throw new DataAccessException("save failed", e)
- 保留原始异常作为 cause,避免堆栈丢失
- 这样既满足重写规则,又不破坏上层调用逻辑,也不强迫调用方改 catch 块


















