1、 異常處理理解:
在Java中,提供了錯誤捕捉和處理機制,即異常處理機制。在程序運行過程中發生錯誤時,Java允許其不按照正常路徑完成任務,由發現錯誤的方法拋出封裝了錯誤信息的對象(異常)到其調用程序,發出已經發生問題的信號,然後立即推出;而且,程序並不在調用該方法的代碼處繼續執行,而是由異常處理機制開始搜索一個能夠處理這種特定錯誤情況的異常處理器。或者手動的去處理出現的異常,達到讓程序正常執行的狀態。
Java異常處理的目的是提高程序的健壯性,你可以在catch和finally代碼塊中給程序一個修正機會,使得程序不因異常而終止或者流程發生以外的改變。同時,通過獲取Java異常信息,也爲程序的開發維護提供了方便,一般通過異常信息就很快就能找到出現異常的問題(代碼)所在。
Java異常處理是Java語言的一大特色,也是個難點,掌握異常處理可以讓寫的代碼更健壯和易於維護。
2、異常分類:
Java所有的異常對象都是一個繼承Throwable類的實例;
Throwable類派生出兩個分支:Error 和 Exception。
a. Error類被認爲是不能恢復的嚴重錯誤,如系統內部錯誤和資源耗盡錯誤等。由於在這種情況下,除了通知用戶並試圖終止程序外幾乎是無能爲力的,因此,不應該拋出這種類型的錯誤,而是讓程序中斷。
在執行該方法期間,無需在方法中通過throws聲明可能拋出但沒有捕獲的 Error 的任何子類,因爲Java編譯器不去檢查它,也就是說,當程序中可能出現這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。
b. Exception類定義可能遇到的輕微錯誤,分爲派生自RuntimeException類中的異常和非派生自RuntimeException類的異常。這時,可以寫代碼來處理異常並繼續程序的執行,而不是讓程序中斷。Java語言規定繼承自Error或RuntimeException的異常成爲未檢查異常,而其他異常則稱爲已檢查異常。
c. 區分RuntimeException和非RuntimeException的規則是:編程錯誤導致RuntimeException,如被零除,數組越界訪問,空指針訪問等;而其他異常則是由於意外情況而發生的,如試圖讀取文件結尾以後的數據、試圖打開錯誤的URL、試圖根據並不代表已存在類的字符串查找Class對象等。
RuntimeException應該由編程者來負責,例如檢查除數是否爲零,對數組訪問要判斷是否下標超出數組邊界,以及在使用前判斷變量是否爲null等。而非RuntimeException的異常則不完全取決於代碼,有時候於環境密切相關,如不同的瀏覽器能夠處理不同的URL等。
3、方法拋出異常 throw 、throws、try...catch...
第一種方式:直接在函數頭中throws SomeException,函數體中不需要try/catch。比如將最開始的例子中的testEx2改爲下面的方式,那麼testEx1就能捕捉到testEx2拋出的異常了。
boolean testEx2() throws Exception{
boolean ret = true;
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}
第二種方式:使用try/catch,在catch中進行一定的處理之後(如果有必要的話)拋出某種異常。例如上面的testEx2改爲下面的方式,testEx1也能捕獲到它拋出的異常: boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}catch (Exception e){
System.out.println("testEx2, catch exception");
Throw e;
}
}
第三種方法:使用try/catch/finally,在catch中進行一定的處理之後(如果有必要的話)拋出某種異常。例如上面的testEx2改爲下面的方式,testEx1也能捕獲到它拋出的異常: boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
throw new Exception("aaa");
}
return true;
}catch (java.lang.ArithmeticException e){
System.out.println("testEx2, catch exception");
ret = false;
throw new Exception("aaa");
}finally{
System.out.println("testEx2, finally; return value="+ret);
}
}
4、 異常捕獲及處理:
對於可能出現異常的代碼,有兩種處理辦法: 第一、在方法中用try...catch語句捕獲並處理異常,catach語句可以有多個,用來匹配多個異常。例如:
public void p(int x){
try{
...
}catch(Exception e){
...
}finally{
...
}
}
第二、對於處理不了的異常或者要轉型的異常,在方法的聲明處通過throws語句拋出異常。例如: public void test1() throws MyException{
...
if(....){
throw new MyException();
}
}
如果每個方法都是簡單的拋出異常,那麼在方法調用方法的多層嵌套調用中,Java虛擬機會從出現異常的方法代碼塊中往回找,直到找到處理該異常的代碼塊爲止。然後將異常交給相應的catch語句處理。如果Java虛擬機追溯到方法調用棧最底部main()方法時,如果仍然沒有找到處理異常的代碼塊,將按照下面的步驟處理:
第一、調用異常的對象的printStackTrace()方法,打印方法調用棧的異常信息。
第二、如果出現異常的線程爲主線程,則整個程序運行終止;如果非主線程,則終止該線程,其他線程繼續運行。
通過分析思考可以看出,越早處理異常消耗的資源和時間越小,產生影響的範圍也越小。因此,不要把自己能處理的異常也拋給調用者。還有一點,不可忽視:finally語句在任何情況下都必須執行的代碼,這樣可以保證一些在任何情況下都必須執行代碼的可靠性。比如,在數據庫查詢異常的時候,應該釋放JDBC連接等等。finally語句先於return語句執行,而不論其先後位置,也不管是否try塊出現異常。finally語句唯一不被執行的情況是方法執行了System.exit()方法。System.exit()的作用是終止當前正在運行的
Java 虛擬機。finally語句塊中不能通過給變量賦新值來改變return的返回值,也建議不要在finally塊中使用return語句,沒有意義還容易導致錯誤。
最後還應該注意一下異常處理的語法規則:
第一、try語句不能單獨存在,可以和catch、finally組成 try...catch...finally、try...catch、try...finally三種結構,catch語句可以有一個或多個,finally語句最多一個,try、catch、finally這三個關鍵字均不能單獨使用。
第二、try、catch、finally三個代碼塊中變量的作用域分別獨立而不能相互訪問。如果要在三個塊中都可以訪問,則需要將變量定義到這些塊的外面。
第三、多個catch塊時候,Java虛擬機會匹配其中一個異常類或其子類,就執行這個catch塊,而不會再執行別的catch塊。
第四、throw語句後不允許有緊跟其他語句,因爲這些沒有機會執行。
第五、如果一個方法調用了另外一個聲明拋出異常的方法,那麼這個方法要麼處理異常,要麼聲明拋出。
5、 如何定義和使用異常類
a. 使用已有的異常類,假如爲IOException、SQLException。
try{
程序代碼
}catch(IOException ioe){
程序代碼
}catch(SQLException sqle){
程序代碼
}finally{
程序代碼
}
b.自定義異常類 創建Exception或者RuntimeException的子類即可得到一個自定義的異常類。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
c.使用自定義的異常
用throws聲明方法可能拋出自定義的異常,並用throw語句在適當的地方拋出自定義的異常。例如:
在某種條件拋出異常
public void test1() throws MyException{
...
if(....){
throw new MyException();
}
}
6、最佳解決方案
對於運行時異常,我們不要用try...catch來捕獲處理,而是在程序開發調試階段,儘量去避免這種異常,一旦發現該異常,正確的做法就會改進程序設計的代碼和實現方式,修改程序中的錯誤,從而避免這種異常。捕獲並處理運行時異常是好的解決辦法,因爲可以通過改進代碼實現來避免該種異常的發生。
對於受檢查異常,沒說的,老老實實去按照異常處理的方法去處理,要麼用try...catch捕獲並解決,要麼用throws拋出!
對於Error(運行時錯誤),不需要在程序中做任何處理,出現問題後,應該在程序在外的地方找問題,然後解決。
7、異常使用的注意事項:
a. 子類覆蓋父類方法時,如果父類的方法有拋出異常,那麼子類覆蓋時,只能拋出該異常或者該異常的子類。
b.父類方法拋出多個異常時,子類覆蓋只能拋出父類異常的子集。
c.有一種情況,只能try,不能throws.
當被覆蓋的方法沒有異常拋出時,子類在覆蓋時,就不可以throws聲明異常。
d.函數中如果拋出了運行時異常RuntimeException或其子類,函數上可以不用throws聲明。
目的就是不讓調用者處理,讓程序停下來,讓調用者對傳遞的數據進行修正。