Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。java
Java類庫中包含許多必須經過調用close
方法手動關閉的資源。 好比InputStream
,OutputStream
和java.sql.Connection
。 客戶常常忽視關閉資源,其性能結果可想而知。 儘管這些資源中有不少使用finalizer機制做爲安全網,但finalizer機制卻不能很好地工做(條目 8)。程序員
從以往來看,try-finally語句是保證資源正確關閉的最佳方式,即便是在程序拋出異常或返回的狀況下:sql
// try-finally - No longer the best way to close resources! 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(); } }
這可能很難相信,但即便是優秀的程序員,大多數時候也會犯錯誤。首先,我在Java Puzzlers[Bloch05]的第88頁上弄錯了,多年來沒有人注意到。事實上,2007年Java類庫中使用close
方法的三分之二都是錯誤的。安全
即便是用try-finally語句關閉資源的正確代碼,如前面兩個代碼示例所示,也有一個微妙的缺陷。 try-with-resources塊和finally塊中的代碼均可以拋出異常。 例如,在firstLineOfFile
方法中,因爲底層物理設備發生故障,對readLine
方法的調用可能會引起異常,而且因爲相同的緣由,調用close
方法可能會失敗。 在這種狀況下,第二個異常徹底沖掉了第一個異常。 在異常堆棧跟蹤中沒有第一個異常的記錄,這可能使實際系統中的調試很是複雜——一般這是你想要診斷問題的第一個異常。 雖然能夠編寫代碼來抑制第二個異常,可是實際上沒有人這樣作,由於它太冗長了。性能
當Java 7引入了try-with-resources語句時,全部這些問題一會兒都獲得瞭解決[JLS,14.20.3]。要使用這個構造,資源必須實現 AutoCloseable
接口,該接口由一個返回爲void
的close
組成。Java類庫和第三方類庫中的許多類和接口如今都實現或繼承了AutoCloseable
接口。若是你編寫的類表示必須關閉的資源,那麼這個類也應該實現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(); } }
如下是咱們的第二個使用try-with-resources的示例:調試
// try-with-resources on multiple resources - short and sweet static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }
不只 try-with-resources版本比原始版本更精簡,更好的可讀性,並且它們提供了更好的診斷。 考慮firstLineOfFile
方法。 若是調用readLine
和(不可見)close
方法都拋出異常,則後一個異常將被抑制(suppressed),而不是前者。 事實上,爲了保留你真正想看到的異常,可能會抑制多個異常。 這些抑制的異常沒有唄被拋棄, 而是打印在堆棧跟蹤中,並標註爲被抑制了。 你也可使用getSuppressed
方法以編程方式訪問它們,該方法在Java 7中已添加到的Throwable
中。code
能夠在 try-with-resources語句中添加catch子句,就像在常規的try-finally語句中同樣。這容許你處理異常,而不會在另外一層嵌套中污染代碼。做爲一個稍微有些作做的例子,這裏有一個版本的firstLineOfFile
方法,它不會拋出異常,可是若是它不能打開或讀取文件,則返回默認值:
// try-with-resources with a catch clause static String firstLineOfFile(String path, String defaultVal) { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } catch (IOException e) { return defaultVal; } }
結論明確:在處理必須關閉的資源時,使用try-with-resources語句替代try-finally語句。 生成的代碼更簡潔,更清晰,而且生成的異常更有用。 try-with-resources語句在編寫必須關閉資源的代碼時會更容易,也不會出錯,而使用try-finally語句其實是不可能的。