前言
最近在看程式碼的時候發現了IO流怎麼都不需要自己手動關閉的呢,還以為是別人忘記關。結果一問,尷尬了。這居然是Java 7 的新特性。因此,特地的記錄下來了。
1. try-with-resources是什麼來的?
try-with-resources,就是Java 7中的新特性之一。其本質是語法糖,在編譯時會進行轉化為 try-catch-finally 語句。編繹器自動在try-with-resources後面增加了判斷物件是否為null,如果不為null,則呼叫close()函式的的位元組碼。
而且在try-with-resources中的變數必須實現AutoCloseable介面.
try-with-resources的語法:
// try-with-resources - the the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
複製程式碼
這麼一看起來,的確提高了可讀性。
2. 為什麼要使用try-with-resources?
至於為什麼要使用try-with-resources。可以看看try-finally使用的情況
在沒有try-with-resources之前,這種語法最能確保關閉資源,無論是丟擲異常還是返回。但是 可能會由於某些原因br.readLine()方法發生異常了,在這種情況下,close()方法呼叫也失敗了,那麼第一個異常會被第二個異常給沖掉,在異常跟蹤棧中也找不到第一個異常的資訊。這對於除錯的時候來說是非常困難的。
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
複製程式碼
但如果在多個資源的情況下呢.變得難看了,不利於閱讀了。
// try-finally is ugly when used with more than one resource!
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
複製程式碼
正是由於以上的兩個原因,try-with-resources出現了。他不需要程式設計師去管理繁瑣的關閉資源,只需要在try-with-resources中的變數實現了AutoableColse介面就可以實現自動關閉資源的效果。
3. try-with-resources有什麼好處?
該語法的出現目的是讓程式設計師不需要再編寫繁瑣的try-finally,並且還能提高程式碼的可讀性。
4. try-with-resources 處理異常?
無論有沒有異常,都會首先執行close(),然後再判斷是否需要進入catch塊.
反編譯後的程式碼:
public static void startTest() {
try {
MyAutoCloseA a = new MyAutoCloseA();
Throwable var33 = null;
try {
MyAutoCloseB b = new MyAutoCloseB();
Throwable var3 = null;
try { // 我們定義的 try 塊
a.test();
b.test();
} catch (Throwable var28) { // try 塊中丟擲的異常
var3 = var28;
throw var28;
} finally {
if (b != null) {
// 如果 try 塊中丟擲異常,就將 close 中的異常(如果有)附加為壓制異常
if (var3 != null) {
try {
b.close();
} catch (Throwable var27) {
var3.addSuppressed(var27);
}
} else { // 如果 try 塊沒有丟擲異常,就直接關閉,可能會丟擲關閉異常
b.close();
}
}
}
} catch (Throwable var30) {
var33 = var30;
throw var30;
} finally {
if (a != null) {
if (var33 != null) {
try {
a.close();
} catch (Throwable var26) {
var33.addSuppressed(var26);
}
} else {
a.close();
}
}
}
// 所有的異常在這裡交給 catch 塊處理
} catch (Exception var32) { // 我們定義的 catch 塊
System.out.println("Main: exception");
System.out.println(var32.getMessage());
Throwable[] suppressed = var32.getSuppressed();
for(int i = 0; i < suppressed.length; ++i) {
System.out.println(suppressed[i].getMessage());
}
}
}
複製程式碼
注意:
- 在catch塊中,是訪問不了try-with-resources中的變數的
- try-with-recourse 中,try 塊中丟擲的異常,在 e.getMessage() 可以獲得,而呼叫 close() 方法丟擲的異常在e.getSuppressed() 獲得。
- try-with-recourse 中定義多個變數時,由反編譯可知,關閉的順序是從後往前。
參考資料: