子类重写父类方法时,检查异常只能缩小或去除,不得扩大;运行时异常可自由增删。父类无throws时,子类不得新增检查异常,应捕获后转为运行时异常封装。

子类重写父类方法时,异常抛出必须严格遵循编译器契约——不能扩大调用方已知的异常范围。核心不是“能不能抛”,而是“抛什么才合法”。
检查异常只能缩小或去掉
父类方法声明了 throws IOException,子类重写时只允许:
- 保持原样:throws IOException
- 换成更具体的子类:throws FileNotFoundException(它是 IOException 的子类)
- 完全不抛:不写 throws 子句
禁止写 throws Exception、throws SQLException(与 IOException 无继承关系)或 throws Throwable——编译器会直接报错:“overridden method does not throw …”。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
运行时异常可自由增删
RuntimeException 及其子类(如 IllegalArgumentException、IllegalStateException)不受重写规则限制:
- 父类没声明,子类可以加 throws IllegalArgumentException(虽不强制,但可作文档提示)
- 父类已声明检查异常,子类还能额外在方法体中 throw new RuntimeException("xxx")
- 这类异常不参与编译检查,不影响多态调用的安全性
父类没声明检查异常,子类不得新增
若父类方法签名是 void doWork()(无 throws),子类重写时:
- 不能写 throws IOException 或任何检查异常,否则编译失败
- 遇到底层可能抛检查异常的操作(如 new FileInputStream()),必须内部处理
- 推荐做法:用 try-catch 捕获后包装为运行时异常,例如
throw new RuntimeException("文件打开失败", e);
设计上优先封装,而非扩大声明
暴露新检查异常往往意味着接口抽象不够。更稳健的做法是:
- 把底层 SQLException、IOException 等统一转为业务层运行时异常(如 DataAccessException)
- 保留原始异常作为 cause,确保堆栈完整、便于排查
- 避免让调用方因子类实现差异而被迫修改异常捕获逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










