zoukankan      html  css  js  c++  java
  • Java7里try-with-resources分析

    这个所谓的try-with-resources,是个语法糖。实际上就是自动调用资源的close()函数。和Python里的with语句差不多。

    例如:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static String readFirstLineFromFile(String path) throws IOException {  
    2.     try (BufferedReader br = new BufferedReader(new FileReader(path))) {  
    3.         return br.readLine();  
    4.     }  
    5. }  


    可以看到try语句多了个括号,而在括号里初始化了一个BufferedReader。

     

    这种在try后面加个括号,再初始化对象的语法就叫try-with-resources。

    实际上,相当于下面的代码(其实略有不同,下面会说明):

     

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {  
    2.     BufferedReader br = new BufferedReader(new FileReader(path));  
    3.     try {  
    4.         return br.readLine();  
    5.     } finally {  
    6.         if (br != null) br.close();  
    7.     }  
    8. }  

     

    很容易可以猜想到,这是编绎器自动在try-with-resources后面增加了判断对象是否为null,如果不为null,则调用close()函数的的字节码。


    只有实现了java.lang.AutoCloseable接口,或者java.io.Closable(实际上继随自java.lang.AutoCloseable)接口的对象,才会自动调用其close()函数。

    有点不同的是Java.io.Closable要求一实现者保证close函数可以被重复调用。而AutoCloseable的close()函数则不要求是幂等的。具体可以参考Javadoc。


    下面从编绎器生成的字节码来分析下,try-with-resources到底是怎样工作的:

     

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public class TryStudy implements AutoCloseable{  
    2.     static void test() throws Exception {  
    3.         try(TryStudy tryStudy = new TryStudy()){  
    4.             System.out.println(tryStudy);  
    5.         }  
    6.     }  
    7.     @Override  
    8.     public void close() throws Exception {  
    9.     }  
    10. }  

    TryStudy实现了AutoCloseable接口,下面来看下test函数的字节码:

     

     

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static test()V throws java/lang/Exception   
    2.   TRYCATCHBLOCK L0 L1 L2   
    3.   TRYCATCHBLOCK L3 L4 L4   
    4.  L5  
    5.   LINENUMBER 21 L5  
    6.   ACONST_NULL  
    7.   ASTORE 0  
    8.   ACONST_NULL  
    9.   ASTORE 1  
    10.  L3  
    11.   NEW TryStudy  
    12.   DUP  
    13.   INVOKESPECIAL TryStudy.<init> ()V  
    14.   ASTORE 2  
    15.  L0  
    16.   LINENUMBER 22 L0  
    17.   GETSTATIC java/lang/System.out : Ljava/io/PrintStream;  
    18.   ALOAD 2  
    19.   INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V  
    20.  L1  
    21.   LINENUMBER 23 L1  
    22.   ALOAD 2  
    23.   IFNULL L6  
    24.   ALOAD 2  
    25.   INVOKEVIRTUAL TryStudy.close ()V  
    26.   GOTO L6  
    27.  L2  
    28.  FRAME FULL [java/lang/Throwable java/lang/Throwable TryStudy] [java/lang/Throwable]  
    29.   ASTORE 0  
    30.   ALOAD 2  
    31.   IFNULL L7  
    32.   ALOAD 2  
    33.   INVOKEVIRTUAL TryStudy.close ()V  
    34.  L7  
    35.  FRAME CHOP 1  
    36.   ALOAD 0  
    37.   ATHROW  
    38.  L4  
    39.  FRAME SAME1 java/lang/Throwable  
    40.   ASTORE 1  
    41.   ALOAD 0  
    42.   IFNONNULL L8  
    43.   ALOAD 1  
    44.   ASTORE 0  
    45.   GOTO L9  
    46.  L8  
    47.  FRAME SAME  
    48.   ALOAD 0  
    49.   ALOAD 1  
    50.   IF_ACMPEQ L9  
    51.   ALOAD 0  
    52.   ALOAD 1  
    53.   INVOKEVIRTUAL java/lang/Throwable.addSuppressed (Ljava/lang/Throwable;)V  
    54.  L9  
    55.  FRAME SAME  
    56.   ALOAD 0  
    57.   ATHROW  
    58.  L6  
    59.   LINENUMBER 24 L6  
    60.  FRAME CHOP 2  
    61.   RETURN  
    62.   LOCALVARIABLE tryStudy LTryStudy; L0 L7 2  
    63.   MAXSTACK = 2  
    64.   MAXLOCALS = 3  


    从字节码里可以看出,的确是有判断tryStudy对象是否为null,如果不是null,则调用close函数进行资源回收。

     

    再仔细分析,可以发现有一个Throwable.addSuppressed的调用,那么这个调用是什么呢?

    其实,上面的字节码大概是这个样子的(当然,不完全是这样的,因为汇编的各种灵活的跳转用Java是表达不出来的):

     

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static void test() throws Exception {  
    2.     TryStudy tryStudy = null;  
    3.     try{  
    4.         tryStudy = new TryStudy();  
    5.         System.out.println(tryStudy);  
    6.     }catch(Throwable suppressedException) {  
    7.         if (tryStudy != null) {  
    8.             try {  
    9.                 tryStudy.close();  
    10.             }catch(Throwable e) {  
    11.                 e.addSuppressed(suppressedException);  
    12.                 throw e;  
    13.             }  
    14.         }  
    15.         throw suppressedException;  
    16.     }  
    17. }  


    有点晕是吧,其实很简单。使用了try-with-resources语句之后,有可能会出现两个异常,一个是try块里的异常,一个是调用close函数里抛出的异常。

     

    当然,平时我们写代码时,没有关注到。一般都是再抛出close函数里的异常,前面的异常被丢弃了。

    如果在调用close函数时出现异常,那么前面的异常就被称为Suppressed Exceptions,因此Throwable还有个addSuppressed函数可以把它们保存起来,当用户捕捉到close里抛出的异常时,就可以调用Throwable.getSuppressed函数来取出close之前的异常了。


    总结:

    使用try-with-resources的语法可以实现资源的自动回收处理,大大提高了代码的便利性,和mutil catch一样,是个好东东。

    用编绎器生成的字节码的角度来看,try-with-resources语法更加高效点。

    java.io.Closable接口要求一实现者保证close函数可以被重复调用,而AutoCloseable的close()函数则不要求是幂等的。


  • 相关阅读:
    GO语言面向对象06---面向对象练习01
    GO语言面向对象05---接口的多态
    GO语言面向对象04---接口的继承
    GO语言面向对象03---接口与断言
    GO语言面向对象02---继承
    Go语言练习---判断闰年以及根据现在的秒数求现在的年月日
    [操作系统] 线程管理
    [操作系统] 进程的状态
    [操作系统] 进程控制块
    关于这次计算机设计大赛
  • 原文地址:https://www.cnblogs.com/songyz/p/6252666.html
Copyright © 2011-2022 走看看