美文网首页
java 7 新特性 try-with-resources

java 7 新特性 try-with-resources

作者: jieyao | 来源:发表于2017-05-24 15:18 被阅读0次

    诸如流、文件等系统资源,当其被打开时,需要手动关闭,要不必将引起一场大灾害。

    传统的try cache finally

    在java7之前,我们可以使用finally来确保资源被关闭,例如:

    static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
      BufferedReader br = new BufferedReader(new FileReader(path));
      try {
        return br.readLine();
      } finally {
        if (br != null) 
            br.close();
      }
    }
    

    但是如果br.readLine()有异常,br.close()也有异常抛出,由于一次只能抛一次异常,try的异常会被抑制,finally的异常会被抛出,导致异常丢失。以下是具体演示:

    public class Connection implements AutoCloseable {
    
        public void sendData() throws Exception {
            throw new Exception("send data");
        }
        @Override
        public void close() throws Exception {
            throw new Exception("close");
        }
    }
    
    public class TryWithResource {
    
        public static void main(String[] args) {
            try{
                test();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static void test()throws Exception{
            Connection conn = null;
            try{
                conn = new Connection();
                conn.sendData();
            } finally{
                if(conn !=null) {
                    conn.close();
                }
            }
        }
    }
    

    运行结果如下:

    java.lang.Exception: close
        at cn.wangjy.study.trywithresources.Connection.close(Connection.java:18)
        at cn.wangjy.study.trywithresources.TryWithResource.test(TryWithResource.java:84)
        at cn.wangjy.study.trywithresources.TryWithResource.main(TryWithResource.java:71)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
    

    用try-with-resources实现

    JAVA 7之后,我们盼来福音,再也不需要在finally里面写一堆if(xxx !=null)xxx.close()的代码,只要资源实现了AutoCloseable或者Closeable接口,try-with-resources能帮其自动关闭。将以上的代码改写为try-with-resources

    public class TryWithResource {
    
        public static void main(String[] args) {
            try(Connection conn =new Connection()) {
                conn.sendData();
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    运行结果如下:

    java.lang.Exception: send data
        at cn.wangjy.study.trywithresources.Connection.sendData(Connection.java:13)
        at cn.wangjy.study.trywithresources.TryWithResource.main(TryWithResource.java:71)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
        Suppressed: java.lang.Exception: close
            at cn.wangjy.study.trywithresources.Connection.close(Connection.java:18)
            at cn.wangjy.study.trywithresources.TryWithResource.main(TryWithResource.java:72)
            ... 5 more
    
    

    可以看到,代码编写也十分简洁。虽然我们没有用finally的代码块写明close(),但是程序仍然会自动执行close()方法,并且异常信息也没有丢失。信息中多了一个Suppressed的提示,附带上close的异常。这个异常其实由两个异常组成, 执行close()的异常是被Suppressed的异常。

    原理是什么呢?

    反编译了TryWithResource的class文件,代码如下

    public static void main(String[] args) {
            try {
                Connection conn = new Connection();
                Throwable localThrowable3 = null;
                try {
                    conn.sendData();
                } catch (Throwable localThrowable1) {
                    localThrowable3 = localThrowable1;
                    throw localThrowable1;
                } finally {
                    if (conn != null) if (localThrowable3 != null) try {
                        conn.close();
                    } catch (Throwable localThrowable2) {
                        localThrowable3.addSuppressed(localThrowable2);
                    }
                    else conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    可见,编译器在编译的时候自动帮我们加了finally块,并添加了资源的close方法,所以例子的close会在运行的时候被执行。细心的你发现了吗,代码中还多了localThrowable3.addSuppressed(localThrowable2)的方法。java7之后,Throwable类新增了addSuppressed方法,支持将一个异常附加到另一个异常身上,从而避免异常屏蔽。这样输出的异常信息会附带Suppressed的提示,参考上图的运行结果。

    try-with-resources 也支持声明多个资源

    public static void main(String[] args) {
            try (FileInputStream fin = new FileInputStream(new File("input.txt")); 
                 FileOutputStream fout = new FileOutputStream(new File("out.txt"));
                 GZIPOutputStream out = new GZIPOutputStream(fout)) {
                byte[] buffer = new byte[4096];
                int read;
                while ((read = fin.read(buffer)) != -1) {
                    out.write(buffer, 0, read);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    资源关闭会按声明时的相反顺序被执行。

    参考:
    http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
    http://www.tuicool.com/articles/JvQRBri

    相关文章

      网友评论

          本文标题:java 7 新特性 try-with-resources

          本文链接:https://www.haomeiwen.com/subject/jphoxxtx.html