java要求catch块按异常类型由子类到父类(具体到抽象)排列,否则父类异常会提前捕获所有子类异常,导致后续catch不可达而编译报错。

Java中,如果在 catch 块中把父类异常写在子类异常前面,编译器会直接报错:“exception XXX has already been caught”。这是因为子类异常会被前面的父类 catch 捕获,后面的子类 catch 永远无法执行,属于不可达代码(unreachable code),违反了 Java 的异常处理规则。
根本原因:异常捕获顺序必须从具体到宽泛
Java 要求 catch 块按**异常类型由子类到父类**(即由具体到抽象)排列。否则,靠前的父类异常会“吞掉”所有子类异常,导致后续 catch 失效。
例如以下写法是错误的:
try {
// 可能抛出 IOException 或 FileNotFoundException
} catch (Exception e) { // ✖ 父类太宽,挡住了所有子类
System.out.println("通用异常");
} catch (FileNotFoundException e) { // ✖ 编译报错:已捕获
System.out.println("文件没找到");
}
正确写法:子类异常放前面,父类放后面
只需调换顺序,让更具体的异常优先被捕获:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 先写
FileNotFoundException、IOException等子类 - 再写
Exception、Throwable等顶层父类(如有必要)
修正后示例:
try {
FileInputStream fis = new FileInputStream("a.txt");
} catch (FileNotFoundException e) { // ✔ 先处理具体异常
System.out.println("文件不存在:" + e.getMessage());
} catch (IOException e) { // ✔ 再处理其他 IO 异常
System.out.println("IO 错误:" + e.getMessage());
} catch (Exception e) { // ✔ 最后兜底(可选)
System.out.println("其他异常:" + e.getMessage());
}
进阶建议:用多异常捕获(Java 7+)简化代码
如果多个子类异常需要执行相同处理逻辑,可用 | 分隔,写在一个 catch 中(注意:它们必须是并列关系,不能有父子继承关系):
- ✅ 合法:
catch (IOException | SQLException e) - ❌ 非法:
catch (Exception | RuntimeException e)(后者是前者的子类)
示例:
try {
// ...
} catch (FileNotFoundException | SecurityException e) {
System.out.println("访问失败:" + e.getClass().getSimpleName());
}
额外提醒:避免过度使用 Exception 或 Throwable
虽然把 Exception 放在最后能兜住一切检查异常,但容易掩盖真正问题:
- 不推荐无差别捕获
Exception后只打印日志或忽略 - 应明确知道哪些异常可能发生,并针对性处理
- 运行时异常(如
NullPointerException)通常不建议显式catch,而应修复逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










