在 Java 7 以前,程序中若是有須要關閉的資源,例如 java.io.InputStream
、java.sql.Connection
等,一般會在 finally 中關閉,例如:html
InputStream inputStream = null; try { inputStream = new FileInputStream("/my/file"); // ... } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
在 Java 7 以及後續版本中,支持 try-with-resources,任何實現 java.lang.AutoCloseable
接口的類,包括 java.io.Closeable
的實現類,均可以經過 try-with-resources 來關閉。java
上面代碼經過 try-with-resources 能夠簡化爲:sql
try (InputStream inputStream = new FileInputStream("/my/file")) { // ... } catch (Exception e) { e.printStackTrace(); }
經過 JDBC 查詢數據庫時,會依次建立 Connection
、Statment
、ResultSet
,而且這三個資源都須要關閉,那麼能夠這樣寫:數據庫
try (Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT ...")) { // ... } catch (Exception e) { // ... }
若是在 try 中定義了多個 resources,那麼它們關閉的順序和建立的順序是相反的。上面的例子中,依次建立了 Connection
、Statment
、ResultSet
對象,最終關閉時會依次關閉 ResultSet
、Statment
、Connection
,因此不用擔憂 Connection
會先 close。segmentfault
官方文檔:oracle
Note that the close methods of resources are called in the opposite order of their creation.
官方文檔:ide
In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.
在 try-with-resources 中,catch 和 finally 中的代碼在資源關閉以後運行。測試
如下是一種錯誤寫法:url
Connection connection = DriverManager.getConnection(url, user, password); try (Connection connection2 = connection) { // ... } catch (SQLException e) { e.printStackTrace(); } finally { try { Statement statement = connection.createStatement(); // 異常,此時 Connection 已關閉 // ... } catch (SQLException ex) { ex.printStackTrace(); } }
在 try-with-resources 中,若是建立資源發生異常,即 try (...)
中小括號裏的代碼出現異常,以及 close 時發生異常,都是會在 catch 中捕捉到的。例如:spa
try (InputStream inputStream = new FileInputStream("/not/exist/file")) { // ... } catch (Exception e) { e.printStackTrace(); // 若是文件不存在,這裏會捕獲異常 }
這段代碼中,若是 new FileInputStream("/not/exist/file")
對應的文件不存在,就會拋出異常,這個異常會在下面的 catch 中捕獲到。
在 try-with-resources 中,若是 try block(即 try 後面大括號中的代碼)拋出異常,會觸發資源的 close,若是此時 close 也發生了異常,那麼 catch 中會捕獲到哪個呢?
因爲 close 拋出異常不是很常見,因此本身實現一個 AutoCloseable
實現類:
public class MyResource implements AutoCloseable { public void doSomething() throws Exception { throw new Exception("doSomething exception"); } @Override public void close() throws Exception { throw new Exception("close exception"); } }
測試代碼:
public class Test { public static void main(String[] args) { try (MyResource myResource = new MyResource()) { myResource.doSomething(); } catch (Exception e) { e.printStackTrace(); } } }
運行結果:
java.lang.Exception: doSomething exception at com.xxg.MyResource.doSomething(MyResource.java:6) at com.xxg.Test.main(Test.java:12) Suppressed: java.lang.Exception: close exception at com.xxg.MyResource.close(MyResource.java:11) at com.xxg.Test.main(Test.java:13)
能夠看到 catch 中捕獲的是 doSomething()
方法拋出的異常,同時這個異常會包含一個 Suppressed Exception,即 close()
方法拋出的異常。
若是想直接拿到 close()
方法拋出的異常,能夠經過 Throwable.getSuppressed()
方法獲取:
try (MyResource myResource = new MyResource()) { myResource.doSomething(); } catch (Exception e) { // e 是 doSomething 拋出的異常,想要拿到 close 方法拋出的異常須要經過 e.getSuppressed 方法獲取 Throwable[] suppressedExceptions = e.getSuppressed(); Throwable closeException = suppressedExceptions[0]; closeException.printStackTrace(); }
AutoCloseable
是 Java 7 新增的接口,Closeable
早就有了。兩者的關係是 Closeable extends AutoCloseable
。兩者都僅包含一個 close()
方法。那麼爲何 Java 7 還要新增 AutoCloseable
接口呢?
Closeable
在 java.io
包下,主要用於 IO 相關的資源的關閉,其 close()
方法定義了拋出 IOException
異常。其實現類實現 close()
方法時,不容許拋出除 IOException
、 RuntimeException
外其餘類型的異常。
而 AutoCloseable
位於 java.lang
包下,使用更普遍。其 close()
方法定義是 void close() throws Exception
,也就是它的實現類的 close()
方法對異常拋出是沒有限制的。