Java裏,對於文件操做IO流、數據庫鏈接等開銷很是昂貴的資源,用完以後必須及時經過close方法將其關閉,不然資源會一直處於打開狀態,直至程序中止,增長系統負擔。java
關閉資源的經常使用方式就是在finally塊裏是釋放,即調用close方法。好比,咱們常常會寫這樣的代碼:數據庫
public static void main(String[] args) { BufferedReader br = null; try { String line; br = new BufferedReader(new FileReader("d:\\testing.txt")); while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { // handle exception } finally { try { if (br != null) { br.close(); } } catch (IOException ex) { // handle exception } } }
能夠看出,爲了關閉資源以及處理關閉資源時可能出現的異常,不得不寫一大推代碼。網絡
從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的代碼,效果以下:ide
public static void main(String[] args) { try(BufferedReader br = new BufferedReader(new FileReader("d:\\testing.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { // handle exception } }
清爽了不少是否是? 可是,有沒有一點不安呢?測試
原來釋放資源的時候若是發生異常,咱們能夠在finally塊中catch新異常,而後繼續處理。可是新方式沒有了finally塊,異常是如何拋出的?若是關閉資源時發生異常怎麼辦?咱們怎麼處理?code
從文檔上能夠找到這樣一段描述:對象
If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.接口
意思是:若是 try 塊拋出異常而且 try-with-resources 語句拋出一個或多個異常,那麼從 try-with-resources 語句中拋出的異常將會被忽略。你能夠經過調用由 try塊拋出的異常的Throwable.getSuppressed 方法檢索這些被忽略的異常信息。資源
在明確一點說就是:文檔
基於這幾種狀況,咱們作幾個測試分別驗證一下:
你們都懂,略。
public class TestTryWithResources { public static void main(String[] args) { try (MyResource resource = new MyResource()) { } catch (Exception e) { System.out.println("捕獲異常: " + e.getMessage()); } } } /** * 自定義一個資源類,close時拋出異常 * * ps:只有實現AutoCloseable或Closeable接口的對象才能用try-with-resources */ class MyResource implements AutoCloseable { @Override public void close() throws Exception { System.out.println("執行close方法,釋放資源"); throw new Exception("釋放資源異常"); } }
執行結果爲: 執行close方法,釋放資源 捕獲異常: 釋放資源異常
即,catch到的是close方法拋出的異常
public class TestTryWithResources { public static void main(String[] args) { try (MyResource resource = new MyResource()) { throw new Exception("try塊異常"); } catch (Exception e) { System.out.println("捕獲異常: " + e.getMessage()); // 找到被忽略的異常 Throwable[] ts = e.getSuppressed(); for(Throwable t : ts) { System.out.println("被忽略的異常"+ t.getMessage()); } } } } /** * 自定義一個資源類,close時拋出異常 */ class MyResource implements AutoCloseable { @Override public void close() throws Exception { System.out.println("執行close方法,釋放資源"); throw new Exception("釋放資源異常"); } }
執行結果: 執行close方法,釋放資源 捕獲異常: try塊異常 被忽略的異常: 釋放資源異常
即,catch到的是try塊中的異常,釋放資源時產生的異常被忽略了。
實際上,不少時候try塊中的異常和close方法拋出的異常是同一類型的。好比流、網絡等不管是try塊仍是close方法都是拋出IOException,咱們該怎麼辦? 最佳實踐:按異常棧最底層的方法名判斷。若是是close方法拋出的異常,就是關閉資源時產生的。 注:咱們的方法名不能叫close
public class TestTryWithResources { public static void main(String[] args) { try (MyResource resource = new MyResource()) { throw new Exception("try塊異常"); } catch (Exception e) { if(!"close".equals(e.getStackTrace()[0].getMethodName())){ System.out.println("處理業務異常"); } } } } /** * 自定義一個資源類,close時拋出異常 */ class MyResource implements AutoCloseable { @Override public void close() throws Exception { System.out.println("執行close方法,釋放資源"); throw new Exception("釋放資源異常"); } }