1. 程式人生 > >重新開始學Java——異常

重新開始學Java——異常

異常 ( Exception ) 也叫例外;在 Java 程式語言中,異常就是程 序在執行過程中由於硬體裝置問題、軟體設計錯誤、缺陷等導致的 程式錯誤,比如: 想開啟的檔案不存在;網路連線中斷;運算元超 出預定範圍;正在裝載的類檔案丟失;訪問的資料庫打不開等操作。

異常是指程式執行過程中出現的非正常事件,如: 字串無法正確 格式化為數字數字(NumberFormatException);在一個取值是 null 的元素上呼叫方法或屬性(NullPointerException);使用了 0 做 除 數 ( 算 術 異 常 ArithmeticException) ; 數 組 下 標 越 界 ( ArrayIndexOutOfBoundsException )......

使用異常的好處

可以實現對已知或未知的"特殊情況"的控制; 可以實現"核心程式" 和 處理"特殊情況"的程式碼相分離

public class ExceptionHandling1 {
	public static void main(String[] args) {
		int a = 100 ;
		int b = 0 ;
		// 因為除數不能是 0,因此 a/b 會發生異常
		// 當異常發生之後,會建立一個該異常對應的物件
		// 即 java.lang.ArithmeticException 型別的物件
		int r = a/b ; // 該行會導致產生java.lang.ArithmeticException 型別的物件
		System.out.println( r );
	}
}

異常處理機制

程式執行過程中發生了異常, 則會生成一個代表該異常的物件。這個異常有可能是當前正在執行的程式造成的,也有可能是虛擬機器生成的;同時當前程式會把這個異常物件交給執行時系統,我們把生成異常物件並把它提交給執行時系統的過程稱為丟擲異常。丟擲異常使用 throw 關鍵字。執行時系統, 通常使用 Runtime 類的例項來表示。執行時系統尋找相應程式碼來處理該異常。

這裡相應的程式碼有兩種情況: 捕獲該異常物件, 並作出相應處理; 對該異常物件不做任何處理, 繼續往外丟擲(比較消極)

捕獲異常

捕獲異常是一種積極的異常處理機制,在 Java 程式執行過程中系統得到一個異常物件時, 它將會沿著方法的呼叫棧逐層回溯, 尋找處理這一異常的程式碼。找到能夠處理這種型別異常的程式碼後, 執行時系統把當前異常物件交給該程式碼進行處理, 這一過程稱作捕獲異常。

捕獲異常使用 catch 關鍵字( 需要跟 try 關鍵字連用)

如果 Java 執行時系統找不到可以捕獲異常的程式碼, 則執行時系統將終止, 相應的 Java 程式也將退出

public class ExceptionHandling2 {
	public static void main(String[] args) {
		int a = 100 ;
		int b = 0 ;
		try{ /** 嘗試執行後邊的程式碼塊,可能有異常發生*/
			int r = a/b ; // 1、該行會導致產生java.lang.ArithmeticException型別的物件
			// 2 、 當前程式將產生的異常物件【丟擲】 ( throw )給“執行時系統”( Runtime 物件)
			System.out.println( r );
		}// 執行時系統把當前異常物件交給該程式碼進行處理,這一過程叫做【捕獲】 (catch)異常
		catch(ArithmeticException e){ /**捕獲異常*/
			// 3 、執行時系統( Runtime物件) 尋找相應的程式碼來處理該異常
			System.out.println("捕獲到的異常: " +e);
		}
	}
}
丟擲異常
public class ExceptionHandling3 {
	/**
	* 在方法宣告上採用throws關鍵字來宣告可能丟擲哪種型別
	的異常
	* @throws ArithmeticException
	*/
	public static void main(String[] args) throws
		ArithmeticException{
		int a = 100 ;
		int b = 0 ;
		int r = a/b ; // 1、該行會導致產生java.lang.ArithmeticException型別的物件
		// 2 、 當前程式將產生的異常物件【丟擲】 ( throw )給“執行時物件”( Runtime 物件)
		// 丟擲的異常物件 : throw e
		// 3 、執行時系統( Runtime物件) 尋找相應的程式碼來處理該異常
		System.out.println( r );
	}
}

Java 中的異常分類

Java 中的異常被分為兩大類:
Runtime 異常 (執行時異常)
	RuntimeException 類及其子類的異常例項被稱為 Runtime 異常
	Runtime 異常無需顯式宣告丟擲
	如果程式需要捕獲 Runtime 異常, 也可以使用 try ... catch 塊來捕獲
Checked 異常 (受檢查異常,檢查工作是交給編譯器處理的)
	不是 RuntimeException 類及其子類的異常例項被稱為 Checked異常
	Java 程式必須顯式處理 Checked 異常
	如果程式沒有處理 Checked 異常, 該程式在編譯時就會發生錯誤
	對於 Checked 異常的處理方式有兩種
		使用 try ... catch 塊來捕獲該異常, 然後在對應的 catch 塊中修補該異常
		當前方法不知道如何處理這種異常, 則定義該方法時宣告丟擲該異常

區別:

執行時異常不受編譯器檢查;受檢查異常,接受編譯器檢查,在編譯器對原始碼編譯成位元組碼的過程中,就要檢查程式設計師是否對“受檢查異常”做出異常(捕獲或丟擲),如果沒有處理這些異常,則編譯器拒絕編譯
/**
* 異常(Exception)的兩個分類
* 執行時異常:繼承過java.lang.RuntimeException類的那些異
常類的例項,不接受編譯器的檢查
* 受檢查異常:從來沒有過繼承RuntimeException類的那些異
常類的例項,對於該型別的異常,編譯器在編譯階段會檢查程式
員是否對這些異常做出處理
* 【注意】:到了執行階段,大家都一樣,該怎麼辦就怎麼辦
*/
public class ExceptionHandling4 {
	public static void main(String[] args) {
		int a = 100 ;91
		int b = 0 ;
		try {
			int r = a /b ; // 可能會導致ArithmeticException異常發生(執行時異常)
			System.out.println("r = "+ r);
		} catch (ArithmeticException e) {
			System.out.println(e);
		}
		Properties pro = new Properties();
		InputStream in = ExceptionHandling4.class.getResourceAsStream("ExceptionHandling3.java");
		try {
			pro.load(in);
		} catch (IOException e) {
			System.out.println(e);
		}
	}
}

異常轉譯

在開發中,經常出現異常,發現 Java 提供的異常不能滿足我們的需求,也就是說看不懂,並不清楚發生了什麼事情,所以我們要進行異常轉譯。下面是我們自己寫的異常

/**
* 自己定義一個異常
* 1、如果繼承RuntimeException或其子類,則是執行時異常
* 2、如果從來沒有繼承RuntimeException類,則是受檢查異常
*/
public class SuanShuException extends Exception{
	public SuanShuException() {
		super(); // 呼叫父類的無參構造
	}
	public SuanShuException(String message, Throwable cause) {
		super(message, cause);// 呼叫父類帶String ,Throwable引數的構造
	}
	public SuanShuException(String message) {
		super(message);// 呼叫父類帶String引數的構造
	}
}
/**
* 異常“轉譯”
* 1、先自己定義一個異常,以便於封裝我們的資訊
* 2、抓去一個可能存在的異常(作為cause傳遞給下一個新建立
的異常物件)
* 3、建立另外一個異常類(比如我們自己定義的那個類),並
為它指定(message、 cause)
* 4、將這個新建立的異常丟擲(throw)
*/
public class ExceptionHandling5 {
/*如果有受檢查異常沒有捕獲,則可以在它所在的方法上用throws關鍵字來宣告丟擲*/
	public static void main(String[] args) throws SuanShuException {
		int a = 100 ;
		int b = 0 ;
		try {
			int r = a /b ; //建立 ArithmeticException物件時,會將發生異常的資訊傳遞進去
			System.out.println("r = "+ r);
		} catch (ArithmeticException e) {
			//e.printStackTrace();//列印堆疊的資訊
			//System.out.println(e);//java.lang.ArithmeticException: / by zero
			//System.out.println(e.getMessage());// / by zero
			// 帶兩個引數表示第一個是提示資訊,第二個引數表示的異常的原因
			SuanShuException suan = new SuanShuException("除數不能為0你都不知道啊" , e);
			// 向外界丟擲一個異常用throw關鍵字
			throw suan; // 丟擲一個具體的異常物件(例項)
		}
	}
}

總結

1、當異常發生時,會產生一個與該異常對應的物件(例項)
2、當前程式會將該異常物件(例項)丟擲給當前的“執行時系統”(Runtime類的一個例項)
3、 “執行時系統”會尋找相應的程式碼來處理該異常處理異常
積極:
try{
	// 可能會引發異常的程式碼
}catch( 異常型別A 變數名){
	// 對異常做出處理
}catch( 異常型別B 變數名){
	// 對異常做出處理
}finally{
	// 無論如何都要執行的程式碼
}
catch之後的()中的型別是有要求的,如果catch之後()內宣告的是IOException,而程式碼中產生的是ArithmeticException型別的異常,則這個catch無法處理這個異常,需要執行時系統再尋找其他處理程式碼

無論是正常執行try中的程式碼,還是發生異常,程式都要繼續向下執行,因此就會有一些程式碼,是無論發生異常還是不發生異常都要執行,這些程式碼在finally中宣告

消極: 可以接著將異常丟擲(可能會導致程式退出)
對於受檢查異常來說,則必須在相應的方法上使用throws來宣告丟擲異常
public void hello() throws 異常型別A , 異常型別B{
}
public void hello() throws 受檢查異常{
throw new 受檢查異常; // 編譯失敗
}
對於執行時異常來說,可以在相應的方法上使用
throws宣告丟擲,也