java异常捕获必须按继承关系由子类到父类排列,否则子类catch块不可达;exception作为顶层父类若写在前面会拦截所有子类异常,导致编译错误。

因为 Java 编译器会从上到下逐个检查 catch 块,一旦某个异常类型能匹配抛出的异常(包括其子类),就立即执行该块,后面的 catch 不再参与匹配。如果把 Exception 写在最前面,它会“吃掉”所有继承自它的子类异常——比如 FileNotFoundException、IOException,导致后续更具体的 catch 块永远无法触发,被编译器判定为不可达代码,直接拒绝编译。
根本原因是类型匹配机制决定的
Java 异常匹配不追求“最精确”,而是“第一个能接住的”。这就像 if-else 链:条件一满足,后面就跳过。Exception 是绝大多数异常的顶层父类(除 Error 和部分 Throwable 直系子类外),相当于一把万能钥匙,插进锁孔就转开了,根本不会留给更专用的钥匙(比如 FileNotFoundException)出场机会。
编译器提前拦截,不是运行时问题
- 这不是逻辑缺陷,而是编译期强制约束——只要 catch 块顺序违反父子关系,哪怕 try 中实际只抛 IOException,编译器也会报错
- IDE(如 IntelliJ、Eclipse)写完立刻标红,提示类似:Unreachable catch block for FileNotFoundException. This exception is already caught by the preceding catch block
- 错误信息明确指出:这个异常已经被前面的 catch 捕获了,后面那行代码永远不会执行
文件流相关异常的正确捕获顺序
读写文件时常见异常有 FileNotFoundException(IOException 的子类)、IOException 本身,以及可能的 SecurityException、NullPointerException 等。正确写法必须按继承关系由窄到宽排列:
- FileNotFoundException(最具体,文件不存在)
- SecurityException(同级,无继承关系,可放这里或按业务频次调整)
- IOException(父类,覆盖其他 IO 问题,如权限不足、磁盘满)
- Exception(兜底,慎用;通常只用于统一日志记录后重新抛出)
不只是 Exception,所有父子异常都适用
这条规则不限于 Exception。只要存在继承关系,子类就必须写在父类前面:
- ✅ 正确:
catch (FileNotFoundException e)→catch (IOException e) - ❌ 错误:
catch (IOException e)→catch (FileNotFoundException e)(编译失败) - 同理适用于 RuntimeException 及其子类:NullPointerException 必须写在 RuntimeException 前面











