異常與資源管理(自動關閉資源細節)

一、使用 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

相關文章
相關標籤/搜索