finally几乎总执行,且在return后、返回前运行;若finally含return会覆盖原返回值;资源释放应优先用try-with-resources,它能自动关闭并压制异常。

try catch finally 的执行顺序和资源释放逻辑
Java 中 try、catch、finally 不是简单“先 try 后 catch 再 finally”,而是有明确的控制流规则:无论是否抛出异常、是否被 catch 捕获,finally 块**几乎总被执行**(唯一例外是 JVM 直接退出,如调用 System.exit())。
关键点在于:finally 在 return 语句之后、实际返回值确定之前执行。如果 finally 中也有 return,它会覆盖 try 或 catch 中的返回值——这是极易踩坑的行为。
- 不要在
finally中写return,否则会掩盖原始异常或返回值 - 资源清理(如关闭
FileInputStream、Connection)应放在finally,但需判空并捕获关闭时可能抛出的新异常 - 若使用 Java 7+,优先用
try-with-resources替代手动finally关闭,更安全且简洁
try-with-resources 比传统 try catch finally 更可靠的原因
try-with-resources 是 Java 7 引入的语法糖,专为自动管理实现了 AutoCloseable 接口的资源而设计。它在字节码层面保证资源在 try 块结束(无论正常还是异常)后被调用 close(),且能正确处理多个资源关闭失败的叠加异常(通过 addSuppressed())。
传统 finally 手动关闭容易漏判空、忽略关闭异常、或因多个 close() 抛异常导致前一个异常被吞掉。
立即学习“Java免费学习笔记(深入)”;
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
return reader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
- 资源声明必须是
final或等效(Java 8+ 允许 effectively final) - 多个资源用分号分隔,关闭顺序与声明顺序相反
- 若
try块抛异常,且close()也抛异常,则后者会被作为 suppressed exception 附加到主异常上,可通过e.getSuppressed()查看
catch 多个异常时的类型顺序和兼容性限制
Java 支持单个 catch 块捕获多个异常类型(用竖线 | 分隔),但前提是这些异常**互不继承**。如果写了 catch (IOException | Exception e),编译器会报错,因为 IOException 是 Exception 的子类。
多异常捕获本质是语法糖,编译后仍生成多个独立的 catch 字节码块,但共享同一份处理逻辑。
- 所有异常类型必须是已检查异常(checked)或运行时异常(unchecked),不能混用泛型类型变量
- 捕获变量
e的静态类型是这些异常的最近公共父类(如IOException | SQLException→ 类型为Exception) - 若需对不同异常做差异化处理,仍应拆成多个
catch块,按从具体到宽泛的顺序排列(例如先SQLException,再IOException,最后Exception)
finally 中发生异常会掩盖原始异常吗
会。如果 try 块抛出异常 A,进入 catch 处理后又抛出异常 B,此时 finally 中再抛出异常 C,则最终抛出的是 C,A 和 B 都被丢弃——除非你在 finally 中显式处理它(比如记录日志、重新抛出但保留原异常)。
这是传统 finally 最危险的陷阱之一。这也是为什么 try-with-resources 被推荐:它内部已处理了 close() 异常的压制逻辑。
- 永远不要在
finally中抛未捕获的异常 - 如果必须在
finally中执行可能失败的操作(如日志写入),请用try-catch包裹,并仅记录错误,不向外抛 - 调试时注意:IDE 或日志中看到的堆栈可能只显示
finally的异常,需检查 suppressed exceptions 或加断点确认原始异常是否被吞
return、throw、close()、suppressed exception 几者交织时,稍不留神就让错误静默消失。








