異常分類
Java把異常當作對象來處理,並定義一個基類java.lang.Throwable作爲所有異常的超類。
Throwable 可以用來表示任何可以作爲異常拋出的類,分爲兩種:
Error
和Exception
。其中 Error 類層次結構描述了Java運行時系統內部錯誤和資源耗盡錯誤。Exception層次結構有分解爲兩個分支:一個分支派生於RuntimeException
;另一個分支包含其他異常。
Java語言規範將派生於
Error類
或RuntimeException類
的所有異常稱爲非受查異常
,所有其他的異常稱爲受查異常
。•
受查異常
:需要用 try…catch… 語句捕獲並進行處理,並且可以從異常中恢復;
•非受查異常
:是程序運行時錯誤,例如除 0會引發 Arithmetic Exception,此時程序崩潰並且無法恢復。
聲明受查異常
一個方法不僅需要告訴編譯器將要返回什麼值,還要告訴編譯器有可能發生什麼錯誤,因此方法應該在其首部聲明所有可能拋出的異常。
例如,標準類庫中提供的FileInputStream類的一個構造器的聲明:
public FileInputStream(String name) throws FileNotFoundException{
這個聲明表示這個構造器可能初始化一個新的FileInputStream對象,也可能拋出一個FileNotFoundException類對象。如果這個方法拋出了這樣一個異常對象,運行時系統就會開始搜索異常處理器,以便知道如何處理異常對象。
總之,一個方法必須聲明所有可能拋出的受查異常,而非受查異常要麼不可控制(Error),要麼就應該避免發生(RuntimeException)。除了聲明異常之外,還可以捕獲異常,這樣會使異常不被拋到方法之外,也不需要throws規範。
異常處理
Java的異常處理本質上是
拋出異常
和捕獲異常
。
①拋出異常
要理解拋出異常,首先要明白什麼是異常情形(ExceptionCondition),它是指阻止當前方法或作用域繼續執行的問題。其次把異常情形和普通問題相區分,普通問題是指在當前環境下能得到足夠的信息,總能處理這個錯誤。對於異常情形,已經無法繼續下去了,因爲在當前環境下無法獲得必要的信息來解決問題,你所能做的就是從當前環境中跳出,並把問題提交給上一級環境,這就是拋出異常時所發生的事情。拋出異常後,會有幾件事隨之發生。首先,是像創建普通的java對象一樣將使用
new
在堆上創建一個異常對象;然後,當前的執行路徑(已經無法繼續下去了)被終止,並且從當前環境中彈出對異常對象的引用。此時,異常處理機制接管程序,並開始尋找一個恰當的地方繼續執行程序,這個恰當的地方就是異常處理程序或者異常處理器,它的任務是將程序從錯誤狀態中恢復,以使程序要麼換一種方式運行,要麼繼續運行下去。
舉個簡單的例子,假使我們創建了一個學生對象Student的一個引用stu,在調用的時候可能還沒有初始化。所以在使用這個對象引用調用其他方法之前,要先對它進行檢查,可以創建一個代表錯誤信息的對象,並且將它從當前環境中拋出,這樣就把錯誤信息傳播到更大的環境中。
if(stu == null){
throw new NullPointerException();
}
②捕獲異常
在方法拋出異常之後,運行時系統將轉爲尋找合適的異常處理器(exception
handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即爲合適的異常處理器。運行時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時系統遍歷調用棧而未找到合適的異常處理器,則運行時系統終止。同時,意味着Java程序的終止。
要捕獲一個異常,必須設置try/catch
語句塊。例如:
try{
InputStream in = new FileInputStream(filename);
}
catch(IOExcetion ie){
System.out.println(ie.getMessage);
}
如果在try語句中任何代碼塊拋出一個在catch子句中說明的異常類或其父類,那麼(1)程序將跳過try語句塊的其餘代碼塊,(2)程序將執行catch子句中處理器代碼。
如果在try語句塊中的代碼沒有拋出任何異常,那麼程序跳過catch子句。
如果方法中的任何代碼拋出了一個在catch子句中沒有聲明的異常類型,那麼這個方法就會立刻退出
在try語句塊中可以捕獲多個異常類型,並對不同類型的異常做出不同的處理。
try{
code that might throw exceptions
}
catch(FileNotFoundException e){
emergency action for missing files
}
catch(UnknowHostException e){
emergency action for unknown hosts
}
catch(IOException e){
emergency action for all other I/O problems
}
假設多種異常的處理動作一樣,就可以合併catch子句:
try{
code that might throw exceptions
}
catch(FileNotFoundException | UnknowHostException e){
emergency action for missing files and unknown hosts
}
catch(IOException e){
emergency action for all other I/O problems
}
注意:
在多個catch子句聲明的異常類型中,父類型異常類必須聲明在子類型異常類之後。例如上面例子中,IOException是FileNotFoundException和UnknowHostException的父類。
欲獲得對象的更多信息,可以使用:
e.getMessage();
得到詳細的錯誤信息,或者使用
e.getClass().getName();
得到異常對象的實際類型。
當異常發生時,通常方法的執行將做一個陡峭的非線性的轉向,它甚至會過早的導致方法返回。例如,如果一個方法打開了一個文件並關閉,然後退出,你不希望關閉文件的代碼被異常處理機制旁路。finally
關鍵字爲處理這種意外而設計。
例如:
InputStream in = new FileInputStream(filename);
try{
code that might throw exception
}
catch(IOException ie){
show error message
}
finally{
in.close();
}
finally塊無論有沒有異常拋出都會執行。如果拋出異常,即使沒有catch子句匹配,finally也會執行。一個方法將從一個try/catch塊返回到調用程序的任何時候,經過一個未捕獲的異常或者是一個明確的返回語句,finally子句在方法返回之前仍將執行。這在關閉文件句柄和釋放任何在方法開始時被分配的其他資源是很有用。
例如:
public static int f(int n){
try{
int r = n * n;
return r;
}
finally{
if(n == 2){
return 0;
}
}
}
在以上方法中,如果調用f(2),那麼try語句塊的計算結果爲r==4,並執行return語句。然而,在方法真正返回前,還要執行finally子句。finally子句將使得方法返回0,這個返回值覆蓋了原始的返回值4.
異常鏈
異常鏈顧名思義就是將異常發生的原因一個傳一個串起來,即把底層的異常信息傳給上層,這樣逐層拋出。 Java API文檔中給出了一個簡單的模型:
try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException) new HighLevelException().initCause(le);
}
當程序捕獲到了一個底層異常,在處理部分選擇了繼續拋出一個更高級別的新異常給此方法的調用者。
這樣異常的原因就會逐層傳遞。這樣,位於高層的異常遞歸調用getCause()方法,就可以遍歷各層的異常原因。
這就是Java異常鏈的原理。異常鏈的實際應用很少,發生異常時候逐層上拋不是個好注意,
上層拿到這些異常又能奈之何?而且異常逐層上拋會消耗大量資源, 因爲要保存一個完整的異常鏈信息.
自定義異常類
在程序中,可能會遇到任何標準異常類都沒有能夠充分地描述清楚地問題。這種情況下,就需要創建自己地異常類。
自定義異常類需要做的就是定義一個派生於Exception的類,或者派生於Exception子類的類。例如定義一個派生於IOException的類,定義的類應該包含兩個構造器,一個默認的構造器;另一個是帶有詳細描述信息的構造器。
class FileFormatException extends IOException{
public FileFormatException(){}
public FileFormatException(String s){
super(s);
}
}
本文參考文獻:
① Java核心技術卷一(第十版)
② Java入門之異常處理
寫在最後:新手上路,如有問題,歡迎大家指出,給意見。如果這篇文章對你有幫助,麻煩幫忙點一下贊,你們小小的舉動能給我無限大的動力。拒絕白嫖,從我做起,從現在做起。Thank you for watching!