java中的try-with-resource语法

打印 上一主题 下一主题

主题 940|帖子 940|积分 2820

java的世界千奇百怪。。。当我甩出如下代码段,不知阁下如何应对?
  1. try(A a=new A()){
  2.     和a变量无关的业务代码块
  3. }
复制代码
没错,这就是“臭名昭著”的try-with-resource语法,乍一看让人不知所云,其实它和try-finally的下述代码等价
  1. A a=new A()
  2. try{
  3.     //业务代码块
  4. }finally{
  5.     a.close();
  6. }
复制代码
本质上就是当使用 try-with-resources 语句块来处理实现了 AutoCloseable 接口的资源时,Java 编译器会在生成的字节码中进行特殊处理。编译器会自动在 try 块结束时插入 finally 块,并在 finally 块中调用 close() 方法来关闭资源。
try-with-resources语法

try-with-resources 语句是一个 Java 7 引入的语法结构,用于自动关闭实现了 AutoCloseable 或 Closeable 接口的资源。它的语法形式如下:
  1. try (resource_declaration) {
  2.     // 使用资源的代码块
  3. } catch (ExceptionType e) {
  4.     // 异常处理逻辑
  5. }
复制代码
在 try-with-resources 语句中,resource_declaration 部分用于声明和初始化一个或多个资源对象。这些资源对象必须实现 AutoCloseable 或 Closeable 接口。
在 try 代码块中,可以使用这些资源对象进行操作。当 try 代码块执行结束时,不论是正常结束还是发生异常,都会自动调用资源对象的 close() 方法来关闭资源。
如果同时声明了多个资源对象,可以使用分号 ; 分隔它们。
那么,它和try-finally语句有什么区别呢?
和try-finally的关系

很明显,try-with-resource语法相对于try-finally语法来说,隐式的调用了资源对象的close方法,语法更简洁,其次,让我们看看如下代码块:
  1. @Test
  2. public void testException(){
  3.     try{
  4.         throw new CustomException("代码块内的业务异常");
  5.     }finally {
  6.         throw new CustomException("finally中的资源释放异常");
  7.     }
  8. }
复制代码
运行上面的代码,你将会得到如下异常:

模仿上述代码,看看在try-with-resource语法中又会怎样
先创建一个类MyAutoClose
  1. @Slf4j
  2. public class MyAutoClose implements AutoCloseable {
  3.     @Override
  4.     public void close() throws Exception {
  5.         log.info("自动关闭");
  6.         throw new CustomException("close方法异常");
  7.     }
  8. }
复制代码
运行如下单元测试
  1. @Test
  2. public void testAutoClose() {
  3.     try (MyAutoClose autoClose = new MyAutoClose()) {
  4.         throw new CustomException("业务异常");
  5.     } catch (Exception e) {
  6.         log.error("", e);
  7.     }
  8. }
复制代码
得到如下异常

综合两种结果来看,可以看到同样的逻辑,业务和finally都是抛出了异常,但是抛出的异常却不一样:在try-finally语句块中,抛出的是finally中的异常,在try-with-resource语句块中,抛出的是业务的异常,而且异常中还携带了close方法关闭时抛出的异常信息。
谁好谁坏,自行体会。
try-with-resources的本质

再进一步细想一下,为啥两种方式逻辑基本一样,但是抛出的异常却不一样?
try-finally代码块很直观,try-with-resource代码块在编译成字节码文件的时候却被编译期“魔改”了,上文提到过
......Java 编译器会在生成的字节码中进行特殊处理。编译器会自动在 try 块结束时插入 finally 块,并在 finally 块中调用 close() 方法来关闭资源。
那只需要通过反编译看下class文件就可以了

再回想一下它原来长什么样子
  1. @Test
  2. public void testAutoClose() {
  3.     try (MyAutoClose autoClose = new MyAutoClose()) {
  4.         throw new CustomException("业务异常");
  5.     } catch (Exception e) {
  6.         log.error("", e);
  7.     }
  8. }
复制代码
亲妈都认不出来了。。。
我将反编译后的真正代码分成了四部分,并标记在了图上

  • 外层异常try-catch块尝试捕获初始化资源异常
  • 里层try-catch块尝试捕获业务执行异常
  • 业务try-catch块捕获到异常,直接抛出
  • finally块判定是否发生了业务异常:如果发生了业务异常,而且close方法执行也发生了异常,则将close方法发生的异常附加业务异常中;如果未发生业务异常,则直接执行close方法,这时候如果发生了异常,直接抛出的就是finally块中的异常了。
总结下,实际上是如下形式
  1. try{
  2.     //初始化资源
  3.     try{
  4.         //业务代码执行
  5.     }catch(Exceptin e){
  6.         //捕获的业务异常,抛出业务异常
  7.     }finally{
  8.         //执行close方法,并判定在不同情况下的异常信息
  9.     }
  10. }catch(Exception e){
  11.     //捕获的资源初始化异常
  12. }
复制代码
结论:使用try-with-resource很明显比使用try-finally块要更好一些,它能准确捕获业务异常;但是try-finally块也有不可替代的使用场景,比如资源类未实现AutoCloseable接口的时候
最后,欢迎关注我的博客原文:https://blog.kdyzm.cn/post/179
END.

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

汕尾海湾

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表