方法重写时受检异常声明不能变宽,必须≤父类范围,否则编译报错;运行时异常无约束;多态调用要求父类声明是唯一可信契约;新增语义应通过运行时异常、前置校验或返回值建模实现。

方法重写时异常声明要正确,核心是守住“受检异常不能变宽”这条线。编译器强制检查,错一点就报红,不是风格问题,而是契约问题。
只对受检异常有约束,运行时异常完全自由
Java 只限制 受检异常(checked exception),也就是继承自 Exception 但不是 RuntimeException 子类的那些异常,比如 IOException、SQLException。
- 父类方法没声明任何受检异常,子类重写时 不能加 throws IOException 等
- 父类声明
throws SQLException,子类可以throws SQLTimeoutException(它是子类),也可以不写throws - 父类没声明异常,子类却抛
IllegalArgumentException或NullPointerException?完全合法,编译器不管
子类 throws 只能缩小或清空,不能扩大或新增
子类方法签名里的 throws 子句,表达的是“我最多可能抛出这些受检异常”。这个范围必须 ≤ 父类声明的范围。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- ✅ 合法:父类
throws Exception→ 子类throws IOException(缩小) - ✅ 合法:父类
throws IOException→ 子类不写throws(清空,内部用 try-catch 处理) - ❌ 错误:父类没写 throws → 子类加
throws FileNotFoundException - ❌ 错误:父类
throws IOException→ 子类throws Exception(扩大到父类)
多态调用是规则存在的根本原因
你写 InputStream is = new FileInputStream(...); is.read();,编译器只看 InputStream.read() 声明的 throws IOException。它不知道背后是哪个子类。
立即学习“Java免费学习笔记(深入)”;
- 如果允许子类擅自加
throws SecurityException,调用方就没法在编译期准备 catch,运行时直接崩 - 所以规则本质是:让父类声明成为调用方唯一可信的异常契约
- 哪怕子类实际抛的是更具体的
FileNotFoundException,调用方也得按IOException来捕获
想表达新错误语义?换种方式,别硬加 throws
当业务需要更多错误类型,又不能破坏接口契约,有更稳妥的做法:
- 抛自定义 运行时异常,如
ValidationFailedException,不触发编译检查,也不影响现有调用 - 用
Objects.requireNonNull()等前置校验把问题拦在入口,避免异常流扩散 - 改用返回值建模,比如
Result<User, Error>,让错误成为可读、可组合的一等公民 - 在 Javadoc 明确写清:“本方法在参数为空时抛
IllegalArgumentException”,形成文档契约

















