try-with-resources語句是一種聲明了一種或多種資源的try語句。資源是指在程序用完了之后必須要關閉的對象。try-with-resources語句保證了每個聲明了的資源在語句結束的時候都會被關閉。任何實現了java.lang.AutoCloseable接口的對象,和實現了java.io.Closeable接口的對象,都可以當做資源使用。
下面的例子讀取了一個文件的第一行。它使用了一個BufferedReader實例去讀取文件,BufferedReader是一種資源,用完之后必須關閉。
static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
在這個例子中,try-with-resources語句種聲明的是BufferedReader資源。聲明語句緊跟着在try關鍵詞的圓括號里面。BufferedReader從Java SE7開始就實現了java.lang.AutoCloseable接口。因為BufferedReader聲明在了try-with-resources里面,所以無論try語句是正常結束還是異常結束(比方說BufferedReader.readLine方法拋出一個IOException異常),它都會被關閉。
在Java SE7之前,你可以用finally代碼塊來確保資源一定被關閉,無論try語句正常結束還是異常結束。下面的例子用finally代碼塊代替try-with-resources語句:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }
然而,在這個例子中,如果readLine和close方法都拋出異常,那么readFirstLineFromFileWithFinallyBlock方法最終會在finally代碼塊拋出異常,從try代碼塊里面拋出的那個異常被抑制住了。相比之下,readFirstLineFromFile方法中,如果try代碼塊和try-with-resources語句同時拋出異常,這個方法將會最終拋出try代碼塊里面的異常,try-with-resources語句里面拋出的異常被壓抑了。在Java SE7之后,你可以找回被壓抑的異常。參看后面的“被壓抑的異常”部分。
你可以在一個try-with-resources語句里面聲明多個資源。下面的例子展示了從一個名字為zipFileName的zip文件中檢索出里面所有文件的名稱,並創建一個文本文件存儲所有文件名稱。
public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws java.io.IOException { java.nio.charset.Charset charset = java.nio.charset.StandardCharsets.US_ASCII; java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName); // Open zip file and create output file with // try-with-resources statement try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // Enumerate each entry for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { // Get the entry name and write it to the output file String newLine = System.getProperty("line.separator"); String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } }
在這個例子中,try-with-resources語句包含了兩個用分號隔開的聲明:ZipFile和BufferedWriter。當代碼塊中代碼終止,不管是正常還是異常,BufferedWriter和ZipFile對象的close方法都會自動按聲明的相反順序調用。
下面的例子用try-with-resources語句自動關閉一個java.sql.Statement對象:
public static void viewTable(Connection con) throws SQLException { String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES"; try (Statement stmt = con.createStatement()) { ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); int supplierID = rs.getInt("SUP_ID"); float price = rs.getFloat("PRICE"); int sales = rs.getInt("SALES"); int total = rs.getInt("TOTAL"); System.out.println(coffeeName + ", " + supplierID + ", " + price + ", " + sales + ", " + total); } } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); } }
例子中的資源java.sql.Statement是JDBC 4.1及其后面版本的一部分。
注意:try-with-resources語句也可以像普通的try語句一樣,有catch和finally代碼塊。在try-with-resources語句中,任何的catch和finally代碼塊都在所有被聲明的資源被關閉后執行。
被壓抑的異常
try-with-resources語句相關聯的代碼塊可能會拋出異常。在writeToFileZipFileContents例子中,try代碼塊中可能會拋出異常,並且有高達兩個異常可能會在try-with-resources語句拋出,當試圖去關閉ZipFile和BufferedWriter對象的時候。如果在try代碼塊中拋出一個異常,同時在try-with-resources語句中拋出一個或多個異常,那么在try-with-resources語句中拋出的異常就被壓抑了,並且最終在writeToFileZipFileContents方法拋出的異常就是try代碼塊中拋出的那個異常。你可以通過被try代碼塊拋出的異常的Throwable.getSuppressed方法找回被壓抑的異常。
實現了AutoCloseable或Closeable接口的類
看
AutoCloseable
和
Closeable
接口的javadoc,看看實現了它們的類都有哪些。Closeable接口繼承了AutoCloseable接口。不同之處在於,Closeable接口的close方法拋出的是IOException異常,AutoCloseable接口的close方法拋出的是Exception異常。 因此,AutoCloseable的子類可以重寫close方法的行為,拋出指定的異常,比如IOException異常,甚至可以不拋出異常。
轉自:http://leaforbook.com/blog/jexception/translate/tryResourceClose.html