java 7 新特性 try-with-resources

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

传统的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 也支持声明多个资源

如下代码:

package org.fenixsoft.clazz;

import org.junit.Test;

import java.io.Closeable;
import java.io.IOException;

/**
 * <b>description</b>: <br>
 * <b>time</b>:2019/6/18 17:53 <br>
 * <b>author</b>:微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!
 */
public class Demo13 implements Closeable {

    @Test
    public void t0() throws IOException {
        try (Demo13 demo13 = new Demo13();) {
            System.out.println("路人甲Java");
        }
    }

    @Test
    public void t1() {
        try {
            try (Demo13 demo13 = new Demo13();
                 Demo13 demo131 = new Demo13();) {
                System.out.println("路人甲Java");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() throws IOException {
        System.out.println("close");
    }
}

反编译之后:

package org.fenixsoft.clazz;

import java.io.Closeable;
import java.io.IOException;
import org.junit.Test;

public class Demo13 implements Closeable {
    public Demo13() {
    }

    @Test
    public void t0() throws IOException {
        Demo13 demo13 = new Demo13();
        Throwable var2 = null;

        try {
            System.out.println("路人甲Java");
        } catch (Throwable var11) {
            var2 = var11;
            throw var11;
        } finally {
            if(demo13 != null) {
                if(var2 != null) {
                    try {
                        demo13.close();
                    } catch (Throwable var10) {
                        var2.addSuppressed(var10);
                    }
                } else {
                    demo13.close();
                }
            }

        }

    }

    @Test
    public void t1() {
        try {
            Demo13 demo13 = new Demo13();
            Throwable var2 = null;

            try {
                Demo13 demo131 = new Demo13();
                Throwable var4 = null;

                try {
                    System.out.println("路人甲Java");
                } catch (Throwable var29) {
                    var4 = var29;
                    throw var29;
                } finally {
                    if(demo131 != null) {
                        if(var4 != null) {
                            try {
                                demo131.close();
                            } catch (Throwable var28) {
                                var4.addSuppressed(var28);
                            }
                        } else {
                            demo131.close();
                        }
                    }

                }
            } catch (Throwable var31) {
                var2 = var31;
                throw var31;
            } finally {
                if(demo13 != null) {
                    if(var2 != null) {
                        try {
                            demo13.close();
                        } catch (Throwable var27) {
                            var2.addSuppressed(var27);
                        }
                    } else {
                        demo13.close();
                    }
                }

            }
        } catch (IOException var33) {
            var33.printStackTrace();
        }

    }

    public void close() throws IOException {
        System.out.println("close");
    }
}

代码中有addSuppressed方法,关于addSuppressed方法的介绍见:java异常处理新特性

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

关注我们