諸如流哪亿、文件等系統(tǒng)資源檩电,當(dāng)其被打開時(shí),需要手動(dòng)關(guān)閉现斋,要不必將引起一場大災(zāi)害喜最。
傳統(tǒng)的try cache finally
在java7之前,我們可以使用finally來確保資源被關(guān)閉庄蹋,例如:
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的異常會(huì)被抑制虫蝶,finally的異常會(huì)被拋出章咧,導(dǎo)致異常丟失。以下是具體演示:
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();
}
}
}
}
運(yùn)行結(jié)果如下:
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實(shí)現(xiàn)
JAVA 7之后能真,我們盼來福音赁严,再也不需要在finally里面寫一堆if(xxx !=null)xxx.close()的代碼,只要資源實(shí)現(xiàn)了AutoCloseable或者Closeable接口舟陆,try-with-resources能幫其自動(dòng)關(guān)閉误澳。將以上的代碼改寫為try-with-resources
public class TryWithResource {
public static void main(String[] args) {
try(Connection conn =new Connection()) {
conn.sendData();
}catch(Exception e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果如下:
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(),但是程序仍然會(huì)自動(dòng)執(zhí)行close()方法裆装,并且異常信息也沒有丟失踱承。信息中多了一個(gè)Suppressed的提示,附帶上close的異常哨免。這個(gè)異常其實(shí)由兩個(gè)異常組成茎活, 執(zhí)行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();
}
}
可見,編譯器在編譯的時(shí)候自動(dòng)幫我們加了finally塊采桃,并添加了資源的close方法懒熙,所以例子的close會(huì)在運(yùn)行的時(shí)候被執(zhí)行。細(xì)心的你發(fā)現(xiàn)了嗎普办,代碼中還多了localThrowable3.addSuppressed(localThrowable2)的方法工扎。java7之后,Throwable類新增了addSuppressed方法衔蹲,支持將一個(gè)異常附加到另一個(gè)異常身上肢娘,從而避免異常屏蔽。這樣輸出的異常信息會(huì)附帶Suppressed的提示舆驶,參考上圖的運(yùn)行結(jié)果橱健。
try-with-resources 也支持聲明多個(gè)資源
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();
}
}
資源關(guān)閉會(huì)按聲明時(shí)的相反順序被執(zhí)行。
參考:
http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
http://www.tuicool.com/articles/JvQRBri