诸如流、文件等系统资源,当其被打开时,需要手动关闭,要不必将引起一场大灾害。
传统的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
网友评论