java中的異常類Exception

異常的定義和使用方法咱就不說了,說點別的,有用的或者好玩的。

 

一、爲什麼要處理異常:
異常是導致程序中斷運行的一種指令流,如果程序中有異常且不做任何處理的話,程序運行到異常處就會中斷執行,直接結束程序,並將錯誤報告給用戶。異常之後的語句也不再執行。爲了保證程序的正常、完整運行,需要對異常進行處理。

 

二、Exception和Error:
Java的異常體系中,最常用的就是Exception和Error,他們都是Throwable的子類。
Exception:一般值程序中出現的問題,可以使用try…catch進行處理,例如經常見到的Exception的子類:NullPointerExcetion,ArrayIndexOutOfBoundsException,NumberFormatException等
Error:一般指JVM錯誤,程序中無法處理,如內存溢出問題。

三、Java的異常處理機制:
處理機制:程序運行的過程中發生異常,JVM會根據異常的類型產生一個異常類的實例化對象,然後在try語句中對此異常對象進行捕捉,並將該對象與catch語句中的各個異常類型進行匹配,如果匹配成功則執行catch語句中的處理程序。
因爲Exception是所有異常類的父類,所以當第一個catch語句定義成Exception類型時,所有的異常將會直接走該catch而不會走下面其他的catch語句,即便其他catch中的異常類型與當前類型一致,因此當定義了Exception的catch語句又定義了其他的異常類型時,Exception的catch語句最好放在最後面,即:捕獲更粗的異常要放在捕獲更細的異常之後。
問題:既然更粗的類型可以捕獲更多的異常,爲什麼不直接使用Throwable??
原因有二:
1、在平常的開發中,Exception中定義的類型基本上可以滿足開發需求,沒有必要使用Throwable;
2、 Throwable中不光有Exception還有Error,對於Error我們是無能爲力的。

個人說明:有時候在開發中會碰到這樣一個問題:代碼一執行到某行,就直接跳轉到catch語句中了,且報出的異常信息莫名其妙,但查看報錯的代碼行根本看不出來有問題甚至會覺得這就是一句類似於1+1=2的代碼,很不理解爲什麼這樣子的代碼還會報錯。
其實代碼本身沒有錯。可以將catch中的異常類型由Exception改成Throwable,然後再打印輸出異常信息,這時候的異常信息纔是導致代碼錯誤的真正原因。原因一般都是導入的jar包有衝突,替換jar就可以解決問題。

 

四、Throws和Throw關鍵字:
Throws:通過放在方法後面,聲明該放方法不處理異常需要調用出自己處理,且告知方法拋出調用出需要處理的異常類型。
Throw:通常放在方法中,在程序中拋出一個異常,且拋出的是異常類的實例化對象。
由兩者的定義可知:Throws拋出的是異常類,Throw拋出的則是異常類的實例。
另外,若是main方法也拋出異常,即:public static void main(String[] args) throws Exception,那麼處理該異常的將會是JVM,也就是調用main方法的Java虛擬機,JVM使用默認的處理方式處理main方法拋出的異常,即中斷代碼的執行。

實例驗證:
要求:設計一個相除的方法,但是在操作之前必須打印「計算開始」字樣,且結束之後打印「計算結束」字樣。如果計算過程中有異常的話,需要將異常交給調用處處理。
思路:
1、定義除法方法div(int i,int j),進行簡單的除法操作:i/j。考慮到傳入的j有可能會是0,所以添加try、catch代碼塊進行捕捉。要求中的打印「計算開始」字樣可以放在i/j之前的try模塊中,而代碼執行的過程中可能也可能不會出現異常,爲保證系統一定打印出「計算結束」字樣,將System.out.println(「計算結束」);放到finally模塊中。當出現異常時,需要將異常交給調用出進行處理,所以catch中不能進行異常處理,而只能將異常拋出,即throw e,因此div方法中也需要標記爲throws Excepion。那麼,除法代碼應該如下:

class Math{
	public int div(int i,int j) throws Exception{//div方法拋出Exception類
		int temp=0;
		try{
			System.out.println("計算開始");
			temp=i/j;//進行除法計算
		}catch(Exception e){
			throw e;//拋出Exception的實例e
		}finally{
			System.out.println("計算結束");
		}
		return temp;//返回結果
	}
}

 2、使用main方法調用上面的div方法,因爲有異常拋出所以需要處理。那麼,代碼應該如下:

public static void main(String[] args){
	Math math = new Math();
	int result=0;
	int result2=0;
	try{
		result = math.div(10,5);//正常數據測試
		System.out.println("10/5="+result);
		System.out.println("------------------------");
		result2  = math.div(10,0);//有異常的數據測試
		System.out.println("10/0="+result2);
	}catch(Exception e){
		System.out.println("發生異常了,異常爲:"+e);
	}
}

3、爲了驗證「無論異常是否發生,catch之後沒有寫在finally中的語句是否執行」,在main方法的最後添加代碼:System.out.println("------------------------------\n無論異常與否這句話還是會輸出");因此完整代碼爲:

class Math{
	public int div(int i,int j) throws Exception{//div方法拋出Exception類
		int temp=0;
		try{
			System.out.println("計算開始");
			temp=i/j;//進行除法計算
		}catch(Exception e){
			throw e;//拋出Exception的實例e
		}finally{
			System.out.println("計算結束");
		}
		return temp;//返回結果
	}
}

public class ExceptionDemo{
	public static void main(String[] args){
		Math math = new Math();
		int result=0;
		int result2=0;
		try{
			result = math.div(10,5);//正常數據測試
			System.out.println("10/5="+result);
			System.out.println("------------------------");
			result2  = math.div(10,0);//有異常的數據測試
			System.out.println("10/0="+result2);
		}catch(Exception e){
			System.out.println("發生異常了,異常爲:"+e);
		}
		System.out.println("------------------------------\n無論異常與否這句話還是會輸出");
	}
}

4、執行結果:

5、程序的執行流程爲:

 五、Exception與RuntimeException:
兩者的區別如下:
Exception在程序中必須使用try…catch進行處理,如果不處理出現異常時程序會被JVM強制中斷。
RuntimeException:可以不使用try…catch進行處理,但是如果有異常產生,那麼異常將由JVM採用默認的處理方式即中斷程序的執行自動進行處理。
簡單實例:

public class RuntimeExceptionDemo{
	public static void main(String[] args){
		String str="123";
		int temp = Integer.parseInt(str);
		System.out.println(temp*temp);
	}
}

以上的代碼看起來沒有問題,運行起來也沒有問題。但是Integer.parse()是有異常拋出的。這是JDK上parseInt方法的說明:

 

public static int parseInt(String s) throws NumberFormatException
它拋出了NumberFormatException,也就是數字轉換異常。
在平常的開發中,若是某個方法拋出異常,調用處必須捕獲該異常或者將異常繼續往上拋出,否則程序將報錯。但是上面的代碼既沒有捕獲也沒有拋出卻執行正確,就是因爲它是RuntimeException,即運行時異常。
異常分爲兩種,一種是編譯時異常,另一種就是運行時異常了。編譯時異常在寫代碼的時候就知道會出現什麼異常,譬如在處理文件流時的I/O問題、文件找不到的問題,你一new File(filePath)IDE就會提醒你這一塊兒有異常,問你try/catch處理還是直接throws。要是即不try也不Throws,那麼在編譯的時候就會直接報錯,強制你處理。運行時異常在編寫代碼的時候不會出現,自然IDE也不會提醒你處理,編譯的時候也不會報錯,但是在運行的時候若參數有問題或其他原因會導致異常的出現,如例子中的若輸入字母就會報NumberFormatException。

 

六、斷言(assert):
定義:斷言就是斷定某一個操作的結果肯定是正確的,如果程序執行到出現斷言語句的時候結果不正確了,那麼通過斷言檢查會爲用戶提示錯誤的信息。
定義格式:
Assert boolean表達式;
Assert boolean表達式:自定義的錯誤提示信息

如下面的例子:

public class TestAssert{
	public static void main(String[] args){
		int i[]={1,2,3};
		assert i.length==0;
	}
}

很明顯,i.length=3而不是等於0,在編譯並執行以後沒有出現任何結果。如圖:

那麼如何讓它處結果也就是起作用呢?可以使用enableassertions。其結構和定義如下:
-enableassertions[:<packagename>...|:<classname>]
               enable assertions
重新驗證看看結果:

以上的錯誤信息是系統默認的,要是覺得不好不能表達準確的錯誤信息,咱們可以自己定義。如:

public class TestAssert{
	public static void main(String[] args){
		int i[]={1,2,3};
		assert i.length==0:"數組長度錯誤";
	}
}

 再進行編譯驗證:
這樣的提示是不是清楚明瞭多了???