Java 7和Java 8之间的细微自动关闭合同更改
Java 7的try-with-resources語句和與該語句一起使用的AutoCloseable類型的一個不錯的功能是,靜態代碼分析工具可以檢測到資源泄漏。 例如,Eclipse:
具有以上配置并嘗試運行以下程序時,您將收到三個警告:
public static void main(String[] args) throws Exception {Connection c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");Statement s = c.createStatement();ResultSet r = s.executeQuery("SELECT 1 + 1");r.next();System.out.println(r.getInt(1)); }輸出是瑣碎的
2警告會在所有c , s和r上發出。 一種快速的解決方案(不要這樣做!)是使用Eclipse特定的SuppressWarnings參數抑制警告:
@SuppressWarnings("resource") public static void main(String[] args) throws Exception {... }畢竟,WeKnowWhatWeReDoing?,這只是一個簡單的示例,對吧?
錯誤!
即使對于簡單的示例(至少在Java 7之后),解決此問題的正確方法是使用輕松的try-with-resources語句。
public static void main(String[] args) throws Exception {try (Connection c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");Statement s = c.createStatement();ResultSet r = s.executeQuery("SELECT 1 + 1")) {r.next();System.out.println(r.getInt(1));} }實際上,如果Eclipse可以自動修復此警告并將所有單獨的語句包裝在try-with-resources語句中,那就太好了。 請支持此功能請求!
Java 8處理了什么?
在Java 8中, AutoCloseable上的合同已非常微妙地更改(或直率地更改了,具體取決于您的觀點)。
Java 7版本
在不再需要時必須關閉的資源。
注意單詞"must" 。
Java 8版本
在關閉之前可以保存資源(例如文件或套接字句柄)的對象。 退出在資源規范頭中已聲明該對象的try-with-resources塊時,將自動調用AutoCloseable對象的close()方法。 這種構造可確保及時釋放,避免資源耗盡異常和可能發生的錯誤。
API注意:
即使并非所有子類或實例都擁有可釋放的資源,基類也有可能并且實際上是常見的。 對于必須完全通用的代碼,或者已知AutoCloseable實例需要釋放資源的代碼,建議使用try-with-resources構造。 但是,當使用諸如Stream的功能同時支持基于I / O和基于非I / O的形式時,使用非基于I / O的形式時通常不需要使用資源嘗試模塊。
簡而言之,從Java 8開始, AutoCloseable更具暗示性,表明您可能正在使用需要關閉的資源,但這并非一定如此。
這類似于Iterable契約,后者沒有說明您只能對Iterable進行一次還是多次迭代,但是它強加了foreach循環所需的契約。
我們什么時候擁有“可選的可關閉”資源?
以jOOQ為例。 與JDBC不同,jOOQ 查詢 ( 在jOOQ 3.7中被設置為AutoCloseable )可能表示資源,也可能不表示資源,這取決于您如何執行。 默認情況下,它不是資源:
try (Connection c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "")) {// No new resources created here:ResultQuery<Record> query =DSL.using(c).resultQuery("SELECT 1 + 1");// Resources created and closed immediatelySystem.out.println(query.fetch()); }輸出再次是:
+----+ | 2| +----+ | 2| +----+但是現在,我們再次在query變量上出現了Eclipse警告,說有一個資源需要關閉,即使通過這種方式使用jOOQ,我們知道事實并非如此。 上面的代碼中唯一的資源是JDBC Connection ,并且已正確處理。 jOOQ內部的jOOQ PreparedStatement和ResultSet已完全處理,并急切地關閉了。
然后,為什么要首先實現AutoCloseable?
jOOQ與JDBC的默認行為相反。
- 在JDBC中,默認情況下所有工作都是延遲進行的,并且必須顯式關閉資源。
- 在jOOQ中,默認情況下會急切地完成所有工作,并且可以有選擇地使資源保持活動狀態。
例如,以下代碼將保持打開的PreparedStatement和ResultSet :
try (Connection c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");// We "keep" the statement open in the ResultQueryResultQuery<Record> query =DSL.using(c).resultQuery("SELECT 1 + 1").keepStatement(true)) {// We keep the ResultSet open in the Cursortry (Cursor<Record> cursor = query.fetchLazy()) {System.out.println(cursor.fetchOne());} }在此版本中,我們在Eclipse中不再有任何警告,但是上述版本實際上是使用jOOQ API時的例外。
Java 8的Stream API也是如此。 有趣的是,Eclipse在這里不發出任何警告:
Stream<Integer> stream = Arrays.asList(1, 2, 3).stream(); stream.forEach(System.out::println);結論
首先,資源泄漏檢測似乎是一個不錯的IDE /編譯器功能。 但是,避免誤報很難。 具體而言,因為Java 8改變了合同AutoCloseable ,實現者被允許執行AutoCloseable為一種方便的契約,而不是作為一種資源存在,必須關閉的清晰指示符。
這使IDE很難(甚至不是不可能)檢測第三方合同(非JDK API)的資源泄漏,而這些合同通常并不為人所知。 與靜態代碼分析工具一樣,該解決方案通常只是關閉潛在的資源泄漏檢測:
- 有關更多的見解,另請參見Stuart Marks的Stack Overflow答案,該鏈接與EG關于lambda-dev的討論相關聯
翻譯自: https://www.javacodegeeks.com/2015/12/subtle-autocloseable-contract-change-java-7-java-8.html
總結
以上是生活随笔為你收集整理的Java 7和Java 8之间的细微自动关闭合同更改的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Project Jigsaw的JDK
- 下一篇: 诸葛菜是什么植物(常被认成萝卜,人称诸葛