一、使用 finally 若是建立 FileInputstream實例就會開啓文檔,不使用時,應該調用 close()關閉文檔。 Fileutil中是經過 Scanner搭配 FileInputstream來讀取文檔,實際上 Scanner()對象有個 close()方法,能夠關閉 Scanner相關資源與搭配的Fileinputstream。 例如:java
package errorDemo; import java.io.*; import java.util.*; public class FileUtil { public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); Scanner scan = new Scanner(new FileInputStream(name)); while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } scan.close(); return txt.toString(); } }
若是 scanner. close()前發生了任何異常,執行流程就會中斷,所以 scanner. close()就可能不會執行,所以 Scanner搭配的 Fileinputstream?就不會被關閉。 你想要的是不管如何,最後必定要執行關閉資源的動做,try、 catch語法還能夠搭配finally,不管try區塊中有無發生異常,若撰寫有 finally區塊,則finally區塊必定會被執行。例如:app
package errorDemo; import java.io.*; import java.util.*; public class FileUtil { public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); Scanner scan = null; try { scan = new Scanner(new FileInputStream(name)); while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } } finally { if(scan!=null) { scan.close(); } } return txt.toString(); } }
因爲finaly區塊必定會被執行,這個範例中 scanner原先是null,若 FileInputStream建立失敗,則 scanner就有可能仍是null,所以在 finally區塊中必須先檢查 scanner是否有參考對象,有的話才進一步調用 close()方法,不然scanner參考至null又打算調用close方法,反而會拋出 NullPointerException。 若是程序撰寫的流程中先return了,並且也有finally區塊,那 finally區塊會先執行完後,再將值返回。例如,下面這個會先顯示 ggg再顯示1:ide
package errorDemo; public class FinallyDemo { public static void main(String[] args) { System.out.println(test(true)); } static int test(boolean flag) { try { if(flag) { return 1; } } finally { // TODO: handle finally clause System.out.println("ggg"); } return 0; } }
二、自動嘗試關閉資源 在使用try、 finally嘗試關閉資源時,會發現程序撰寫的流程是相似的,就如先前 FileUtil,你會先檢查 scanner是否爲null,再調用close()方法關閉 Scanner。在JDK7以後,新增了嘗試關閉資源 (try-with- Resources)語法ui
package errorDemo; import java.io.*; import java.util.*; public class FileUtil2 { public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); try (Scanner scan = new Scanner(new FileInputStream(name))) { while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } } return txt.toString(); } }
想要嘗試自動關閉資源的對象,是撰寫在try以後的括號中,若是無須 catch處理任何異常,能夠不用撰寫,也不用撰寫finally自行嘗試關閉資源。JDK7的嘗試關閉資源語法是編譯程序蜜糖,嘗試反編譯:code
... public class FileUtil2 { public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); Scanner scan = new Scanner(new FileInputStream(name)); Throwable localThrowable2 = null; try{ while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } }catch(Throwable LocalThrowable1){//捕捉全部錯誤 localThrowable2 = LocalThrowable1; throw LocalThrowable1; } finally{ if(scan!=null){//若是scan參考了Scanner實例 if(localThrowable2!=null){//若前面catch到其餘異常 try{ scan.close();//嘗試關閉Scanner實例 }catch(Throwable x2){//萬一關閉時發生錯誤 localThrowable2.addSuppressed(x2);//在原異常對象中記錄 } }else{ scan.close();//若前面沒有發生任何異常,直接關閉 } } } return txt.toString(); } }
若一個異常被 catch後的處理過程引起另外一個異常,一般會拋出第一個異常做爲響應, addSuppresse()方法是JDKT在java.lang.Throwable中新增的方法,可將第二個異常記錄在第一個異常之中,JDK7中與之相對應的是 getSuppressed()方法,可返回 Throwable[],表明先前被addSuppressed()記錄的各個異常對象。
使用自動嘗試關閉資源語法時,也能夠搭配 catch。如在發生FileNotFoundException 時顯示堆棧追蹤信息:對象
public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); try (Scanner scan = new Scanner(new FileInputStream(name))) { while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } }catch(FileInputStream e){ e.printStackTrace(); throw e; } return txt.toString(); }
使用JAD反編譯後能夠看到,實際上前一個反編譯程序片斷中一部分,是產生在另外一個try、 catch區塊中:接口
... public class FileUtil2 { public static String readFile(String name) throws FileNotFoundException { StringBuilder txt = new StringBuilder(); try{ Scanner scan = new Scanner(new FileInputStream(name)); Throwable localThrowable2 = null; try { while (scan.hasNext()) { txt.append(scan.nextLine()).append('\n'); } }catch(Throwable LocalThrowable1){ localThrowable2 = LocalThrowable1; throw LocalThrowable1; } finally{ if(scan!=null){ if(localThrowable2!=null){ try{ scan.close(); }catch(Throwable x2){ localThrowable2.addSuppressed(x2); } }else{ scan.close(); } } } }catch(FileInputStream ex){ ex.printStackTrace(); throw ex; } return txt.toString(); } }
使用自動嘗試關閉資源語法時,並不影響你對特定異常的處理。自動嘗試關閉資源語法僅協助你關閉資源,而非用於處理異常。從反編譯的程序代碼中也能夠看到,使用嘗試關閉資源語法時,不要試圖自行撰寫程序代碼關閉資源,這樣會形成重複調用close()方法。 3 、java. lang. AutoCloseable JDK7的嘗試關閉資源語法可套用的對象,必須操做java.lang. AutoCloseable接口。它是JDK7新增的僅定義了close()的接口。 只要操做AutoCloseable接口就能夠套用至嘗試自動關閉資源語法:資源
package errorDemo; public class AutoCloseableDemo { public static void main(String[] args) { try (Resource res=new Resource()){//括號內執行完自動關閉 res.doSome(); } catch (Exception e) { e.printStackTrace(); } } } class Resource implements AutoCloseable{ void doSome() { System.out.println("gggg"); } @Override public void close() throws Exception { System.out.println("資源關閉"); } }
執行結果:gggg 資源關閉 嘗試自動關閉資源語法能夠關閉兩個以上對象資源,中間以分號分開:文檔
package errorDemo; public class AutoCloseableDemo { public static void main(String[] args) { try (Resource res = new Resource(); Resource2 res2 = new Resource2()) {//try區間執行完畢後統一關閉資源 res.doSome(); res2.doOther(); } catch (Exception e) { e.printStackTrace(); } } } class Resource implements AutoCloseable { void doSome() { System.out.println("some"); } @Override public void close() throws Exception { System.out.println("資源關閉"); } } class Resource2 implements AutoCloseable { void doOther() { System.out.println("other"); } @Override public void close() throws Exception { System.out.println("資源2關閉"); } }
執行結果:some other 資源2關閉 資源關閉 try括號中,越後面的對象資源越早被關閉。反編譯發現每一個AutoCloseable對象,都獨立使用一個try、catch、finally,try括號越後面的對象會越在內層的try、catch、finally中。get