try-with-resources 語句
Version >= Java SE 7
正如 try-catch-final 語句示例所示,使用 finally
子句進行資源清理需要大量的樣板程式碼才能正確實現邊緣情況。Java 7 以 try-with-resources 語句的形式提供了一種更簡單的方法來處理這個問題。
什麼是資源?
Java 7 引入了 java.lang.AutoCloseable
介面,允許使用 try-with-resources 語句管理類。實現 AutoCloseable
的類的例項稱為資源。這些通常需要及時處理,而不是依靠垃圾收集器來處理它們。
AutoCloseable
介面定義了一個方法:
public void close() throws Exception
close()
方法應以適當的方式處理資源。規範宣告在已經處理的資源上呼叫該方法應該是安全的。另外,強烈建議實現 Autocloseable
的類宣告 close()
方法丟擲比 Exception
更具體的異常,或者根本沒有例外。
各種標準 Java 類和介面實現了 AutoCloseable
。這些包括:
InputStream
,OutputStream
及其子類Reader
,Writer
及其子類Socket
和ServerSocket
及其子類Channel
及其子類,和- JDBC 介面
Connection
,Statement
和ResultSet
及其子類。
應用程式和第三方類也可以這樣做。
基本的 try-with-resource 語句
try-with-resources 的語法基於經典的 try-catch , try-finally 和 try-catch-finally 形式。這是一個基本形式的例子; 即沒有 catch
或 finally
的形式。
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
在 try
子句之後,要管理的資源在 (...)
部分中宣告為變數。在上面的例子中,我們宣告瞭一個資源變數 stream
並將其初始化為新建立的 PrintStream
。
資源變數初始化後,執行 try
塊。完成後,將自動呼叫 stream.close()
以確保資源不會洩漏。請注意,無論塊如何完成,都會發生 close()
呼叫。
增強的 try-with-resource 語句
在試穿與資源語句可以用 catch
和 finally
塊增強,與 Java 的前 7 的 try-catch-終於語法。下面的程式碼片段為我們之前的程式碼片段新增了一個 catch
塊來處理 PrintStream
建構函式可以丟擲的 FileNotFoundException
:
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
} catch (FileNotFoundException ex) {
System.err.println("Cannot open the file");
} finally {
System.err.println("All done");
}
如果資源初始化或 try 塊丟擲異常,則將執行 catch
塊。finally
塊將始終執行,就像傳統的 try-catch-finally 語句一樣。
但有幾點需要注意:
- 資源變數超出了
catch
和finally
塊的範圍。 - 資源清理將在語句嘗試匹配
catch
塊之前發生。 - 如果自動資源清理引發異常,則可以在其中一個
catch
塊中捕獲。
管理多個資源
上面的程式碼段顯示了一個被管理的資源。事實上, try-with-resources 可以在一個語句中管理多個資源。例如:
try (InputStream is = new FileInputStream(file1);
OutputStream os = new FileOutputStream(file2)) {
// Copy 'is' to 'os'
}
這表現得像你期望的那樣。is
和 os
都在 try
塊結束時自動關閉。有幾點需要注意:
- 初始化發生在程式碼順序中,後來的資源變數初始化程式可以使用先前的值。
- 將清除已成功初始化的所有資源變數。
- 資源變數按其宣告的相反順序進行清理。
因此,在上面的例子中,is
在 os
之前被初始化並且在它之後被清理,並且如果在初始化 os
時存在異常則將清除 is
。
try-with-resource 和經典 try-catch-finally 的等價性
Java 語言規範根據經典的 try-catch-finally 語句指定 try-with-resource 表單的行為。 (有關詳細資訊,請參閱 JLS。) **
例如,這個基本的資源嘗試 :
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
被定義為等同於此 try-catch-finally :
// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");
// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;
// The actual try block
try {
stream.println("Hello world!");
} catch (Throwable t) {
// If an exception is thrown, remember it for the finally block
primaryException = t;
throw t;
} finally {
if (primaryException == null) {
// If no exception was thrown so far, exceptions thrown in close() will
// not be caught and therefore be passed on to the enclosing code.
stream.close();
} else {
// If an exception has already been thrown, any exception thrown in
// close() will be suppressed as it is likely to be related to the
// previous exception. The suppressed exception can be retrieved
// using primaryException.getSuppressed().
try {
stream.close();
} catch (Throwable suppressedException) {
primaryException.addSuppressed(suppressedException);
}
}
}
(JLS 指定實際的 t
和 primaryException
變數對於普通的 Java 程式碼是不可見的。)
增強形式的 try-with-resources 被指定為與基本形式的等價。例如:
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}
相當於:
try {
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
}
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}