try-with-resource是從java7開始提供的新特性。方便了我們資源的代碼編寫。下面展示一下代碼模板的對比
沒有try-with-resource
try{
資源打開
業(yè)務(wù)邏輯
}catch(Exception e){
異常處理
}finally{
資源關(guān)閉
}
try-with-resource的
try(資源打開){
業(yè)務(wù)邏輯
}catch(Exception e){
異常處理
}
案例分析
看了上面的模板,我們的一個直觀感覺是省去了finally,并且把資源放在了try后面的括號中。下面展示一段真實的代碼腔剂。
public static void main(String[] args) {
try (
InputStream fileInputStream = Files.newInputStream(Paths.get("/Users/xie/delete/test.txt"));
OutputStream outputStream = Files.newOutputStream(Paths.get("/Users/xie/delete/test2.txt"));
) {
byte[] bytes = new byte[1024];
int len;
while ((len = fileInputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
注:上面的代碼只是演示了一個文件拷貝的方法,如果我們自己寫代碼請選擇1.7提供的Files.copy驼仪。
try-with-resource做了什么
我們通過反編譯工具來做一下對比
public static void main(String[] args) {
try {
InputStream fileInputStream = Files.newInputStream(Paths.get("/Users/xie/delete/test.txt"));
Throwable var2 = null;
try {
OutputStream outputStream = Files.newOutputStream(Paths.get("/Users/xie/delete/test2.txt"));
Throwable var4 = null;
try {
byte[] bytes = new byte[1024];
int len;
while((len = fileInputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
} catch (Throwable var30) {
var4 = var30;
throw var30;
} finally {
if (outputStream != null) {
if (var4 != null) {
try {
outputStream.close();
} catch (Throwable var29) {
var4.addSuppressed(var29);
}
} else {
outputStream.close();
}
}
}
} catch (Throwable var32) {
var2 = var32;
throw var32;
} finally {
if (fileInputStream != null) {
if (var2 != null) {
try {
fileInputStream.close();
} catch (Throwable var28) {
var2.addSuppressed(var28);
}
} else {
fileInputStream.close();
}
}
}
} catch (IOException var34) {
var34.printStackTrace();
}
}
基本從字節(jié)碼反編譯過來掸犬,我們可以看到他是按照我們熟悉的方式編寫的代碼袜漩。所以try-with-resource是語法糖。
這里大家不熟悉的可能就是一場處理里面有addSuppressed的調(diào)用湾碎。這是一個異常新加的方法噪服,抑制異常。其實這種場景很常見胜茧,一個方法里執(zhí)行出了多個異常,應(yīng)該報哪個呢仇味,雖然都能表示這個方法調(diào)用的失敗呻顽。以前的做法就是自己catch然后做一些邏輯操作,最后拋出一個丹墨。有了這個方法就能實現(xiàn)以前的邏輯廊遍,邏輯中也可以取出被抑制的異常信息。
使用的注意事項
- 第三方庫的資源使用需要了解贩挣,他的資源是否實現(xiàn)了Closeable喉前。
jdk的資源是都實現(xiàn)了這個接口。
class OutputStream implements Closeable, Flushable {
只有實現(xiàn)了Closeable的才可以和try-with-resource搭配使用王财。
- 需要了解每個資源的關(guān)閉細(xì)節(jié)
這里需要列舉兩個情況卵迂。
- socket的流的關(guān)閉會導(dǎo)致socket關(guān)閉。
下面以inputstream為例绒净。SocketInputStream集成FileInputStream见咒,所以也實現(xiàn)了Closeable。
class SocketInputStream extends FileInputStream
但是他的close方法做的事情有點多挂疆,會檢測socket是否關(guān)閉改览。
public void close() throws IOException {
if (closing)
return;
closing = true;
if (socket != null) {
if (!socket.isClosed())
socket.close();
} else
impl.close();
closing = false;
}
- 輸出流關(guān)閉沒有flush
這是我調(diào)用別人寫的庫的時候遇到的問題,一直發(fā)現(xiàn)文件超過一定大小就會傳輸丟失缤言。習(xí)慣了很多庫都是close會順帶幫你做flush宝当。
上面的這些情況,本身就是使用流該注意的地方胆萧。
- 資源聲明順序
通過上面的例子庆揩,其實資源也是和我們自己寫代碼的思路一致,先聲明的后關(guān)閉跌穗。
9版本的改進(jìn)
java9之前的try-with-resource都是必須做一次賦值的盾鳞。
public static void read(InputStream fileInputStream) {
try (InputStream fileInputStreamTmp = fileInputStream) {
byte[] bytes = new byte[1024];
int len;
while ((len = fileInputStreamTmp.read(bytes)) > 0) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
這個問題也挺明顯的,我都開始名字上加Tmp來標(biāo)識了瞻离。
9之后就徹底不用這么做了
public static void read(InputStream fileInputStream) {
try (fileInputStream) {
byte[] bytes = new byte[1024];
int len;
while ((len = fileInputStream.read(bytes)) > 0) {
}
} catch (IOException e) {
e.printStackTrace();
}
}