try-catch-finally 与 try-with-resources
传统的 try-catch-finally 结构虽然能够实现资源的正确关闭,但代码冗长。
try-with-resources(自动资源管理) 语法糖,在声明或者使用使用完资源后,不再需要手动编写关闭资源的代码,实现自动调用close()方法关闭,并使代码更加清晰明了。
多资源关闭时,其中一个资源时抛出了异常,则该异常将不会影响其他资源正常关闭。
但这同样带来了一些问题,笔记先阐述使用方式与基本概念,在阐述一些隐藏的问题。
- 异常抑制
- 自动关闭了一些不该关闭的流
- 此语法糖的Class必须实现AutoCloseable接口
(AutoCloseable接口只有一个close方法来实现自动关闭)
// 代码简洁 自动关闭
// try-with-resources
try (InputStream inputStream = new FileInputStream("file.txt")) {
// 使用inputStream进行操作
} catch (IOException e) {
// 异常处理
}
// 传统的try-catch-finally
InputStream inputStream = null;
try {
inputStream = new FileInputStream("file.txt");
// 使用inputStream进行操作
} catch (IOException e) {
// 异常处理
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// 关闭资源时的异常处理
}
}
}
// 多资源关闭回收
try (InputStream input = new FileInputStream("input.txt");
OutputStream output = new FileOutputStream("output.txt")) {
// 使用input和output进行操作
} catch (IOException e) {
// 异常处理
}
AutoCloseable 接口
使用 try-with-resources 时,无需手动关闭实现了
AutoCloseable
接口的资源。在 try 代码块执行完毕后,系统会自动调用资源的close()
方法来关闭资源。(你也可以自己实现该接口)
如果有需要,也可以使用try catch resources也可以继续使用finally在其中进行打印操作等
异常抑制的产生
正常情况下,如果try代码块中抛出了异常,则控制权转移到catch部分,在控制权跳转过程中,该资源会自动调用close()方法。 但是如果在用close()方法关闭该资源时,可能会抛出一个异常,该异常将被抑制; 而try代码块中抛出的异常,会被try-with-resources语句真正抛出。
类似传统的try-catch-finally语句块中也存在异常抑制。 如果在try语句块中抛出了异常,在控制权转移到调用栈上一层代码之前,finally语句块中的语句也会执行。 但是finally语句块在执行的过程中,也可能会抛出异常。如果finally语句块也抛出了异常,那么这个异常会往上传递,而之前try语句块中的那个异常就丢失了。
可以看到try-with-resources编译后的代码,如果try块与自动生成的close()方法中都出现了异常,那么抛出的只有try块中的异常,而close()方法中的异常只是被Throwable.addSuppressed()记录了下来,并不会被直接抛出。
处理方法
Java 7引入了Suppressed Exceptions的概念 通过Throwable.getSuppressed()来获取
try (ResourceType resource = new ResourceType()) {
// 在try块中操作资源,可能抛出异常
} catch (ResourceOperationException e) {
// 捕获并处理资源关闭时的异常
Throwable[] suppressed = e.getSuppressed();
for (Throwable t : suppressed) {
// 处理或记录抑制的异常
}
}
自动关闭问题
- try-with-resources 特性会先关闭流,再抛出异常,在使用时要注意,要考虑出异常时,这个流可不可以关闭。
- 流关闭后,数据再也写不到缓冲区中,同时 nio 的 堆内内存缓存区 HeapByteBuffer 中的数据仍然是旧的。后面不管怎么 flush 都无法给到有效反馈信息给前端。
try-with-resources变量
- try-with-resource中声明的变量会隐式的加上final 关键字,无法被更改 为了防止在try块中改变了变量的引用,导致关闭资源失效
JDK9优化
- JDK7/8只能在try中声明,JDK9中可以先声明 ,把变量放在try中即可实现自动回收
总结
在处理必须关闭的资源时,使用 try-with-resources 语句替代 try-finally 语句没有任何问题。
生成的代码更简洁,更清晰,并且生成的异常更有用,也不会出错。
当有一些资源出现异常后并不能关闭的时候,使用该语法糖就要注意了。
References
- 基本概念: try-with-resources
- 比较官方的解释:CSDN博客 - 专业IT技术发表平台
- 流自动关闭问题:【Java记录】try-with-resources的一个坑 - Xesam - OSCHINA - 中文开源技术交流社区
- try-with-resource中声明的变量问题:【java】try-with-resource使用时遇到的问题_try-with 编译报错
- getSuppressed() 使用案例:java.lang.Throwable.getSuppressed()方法的使用及代码示例_其他_大数据知识库