標籤:異常 java
異常結構:
- 異常的繼承結構:Throwable為基類,Error和Exception繼承Throwable。
- RunTimeException,IOException,SQLException等繼承Exception;IOError,VirtualMachineError等繼承Error。
- Error和RuntimeException及其子類成為未檢查異常(unchecked),其它異常成為已檢查異常(checked)。
Error異常:
Error 是 Throwable 的子類,用於指示合理的應用程式不應該試圖捕獲的嚴重問題。在這種情況下應用程式只能中止運行,例如JAVA 虛擬機器出現錯誤。Error是一種unchecked Exception,編譯器不會檢查Error是否被處理,在程式中不用捕獲Error類型的異常。
RuntimeException異常:
Exception異常包括RuntimeException異常和其他非RuntimeException的異常。
RuntimeException 是一種Unchecked Exception,即表示編譯器不會檢查程式是否對RuntimeException作了處理,在程式中不必捕獲RuntimException類型的異常,也不必在方法體聲明拋出RuntimeException類。
RuntimeException發生的時候,表示程式中出現了編程錯誤,所以應該找出錯誤修改程式,而不是去捕獲RuntimeException。
Checked Exception異常:
Checked Exception異常,這也是在編程中使用最多的Exception,所有繼承自Exception並且不是RuntimeException的異常都是checked Exception,中的IOException和ClassNotFoundException。
JAVA 語言規定必須對checked Exception作處理,編譯器會對此作檢查,要麼在方法體中聲明拋出checked Exception,要麼使用catch語句捕獲checked Exception進行處理,不然不能通過編譯。
在聲明方法時候拋出異常:
文法:throws(略)(method throws Exception1,Exception2,..,ExceptionN{})
為什麼要在聲明方法拋出異常?
方法是否拋出異常與方法傳回值的類型一樣重要。假設方法拋出異常卻沒有聲明該方法將拋出異常,那麼客戶程式員可以調用這個方法而且不用編寫處理異常的代碼。那麼,一旦出現異常,那麼這個異常就沒有合適的異常控制器來解決。
如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。
為什麼拋出的異常一定是已檢查異常?
RuntimeException與Error可以在任何代碼中產生,它們不需要由程式員顯示的拋出,一旦出現錯誤,那麼相應的異常會被自動拋出。遇到Error,程式員一般是無能為力的;遇到RuntimeException,那麼一定是程式存在邏輯錯誤,要對程式進行修改;只有已檢查異常才是程式員所關心的,程式應該且僅應該拋出或處理已檢查異常。而已檢查異常是由程式員拋出的,這分為兩種情況:客戶程式員調用會拋出異常的庫函數;客戶程式員自己使用throw語句拋出異常。
注意:重寫方法不能拋出新的異常或者比被重寫方法聲明的檢查異常更廣的檢查異常。但是可以拋出更少,更有限或者不拋出異常。
在方法中拋出異常:
文法:throw(略):
throw總是出現在函數體中,用來拋出一個Throwable類型的異常(throw new exceptionname)。程式會在throw語句後立即終止,它後面的語句執行不到,然後在包含它的所有try塊中(可能在上層調用函數中)從裡向外尋找含有與其匹配的catch子句的try塊。
拋出什麼異常?
對於一個異常對象,真正有用的資訊是異常的物件類型,而異常對象本身毫無意義。比如一個異常對象的類型是ClassCastException,那麼這個類名就是唯一有用的資訊。所以,在選擇拋出什麼異常時,最關鍵的就是選擇異常的類名能夠明確說明異常情況的類。
try-catch-finally語句:
try { // 可能會發生異常的程式碼 (監控區)} catch (Type1 id1) { // 捕獲並處理try拋出的異常類型Type1 } catch (Type2 id2) { // 捕獲並處理try拋出的異常類型Type2 } finally { // 無論是否發生異常,都將執行的語句塊 }
try關鍵詞後的一對大括弧將一塊可能發生異常的程式碼封裝起來,稱為監控地區。Java方法在運行過程中出現異常,則建立異常對象。將異常拋出監控地區之外;
異常拋出監控地區之外後,由Java運行時系統試圖尋找匹配的catch子句以捕獲異常。若有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束;
必須注意的是:一旦某個catch捕獲到匹配的異常類型,將進入異常處理代碼。一經處理結束,就意味著整個try-catch語句結束。其他的catch子句不再有匹配和捕獲異常類型的機會。所以,對於有多個catch子句的異常程式而言,應該盡量將捕獲底層異常類的catch子 句放在前面,同時盡量將捕獲相對高層的異常類的catch子句放在後面。否則,捕獲底層異常類的catch子句將可能會被屏蔽。
finally關鍵字保證無論程式使用任何方式離開try塊,finally中的語句都會被執行。當你需要一個地方來執行在任何情況下都必須執行的代碼時,就可以將這些代碼放入finally塊中。當要把除了記憶體之外的資源恢複到它們的初始狀態時,就要用到finally子句。這種需要清理的資源套件括:已經開啟的檔案或網路連接,在螢幕上顯示的圖形等。
必須注意的是:在finally塊中不能拋出異常。JAVA異常處理機制保證無論在任何情況下必須先執行finally塊然後再離開try塊,因此在try塊中發生異常的時候,JAVA虛擬機器先轉到finally塊執行finally塊中的代碼,finally塊執行完畢後,再向外拋出異常。如果在finally塊中拋出異常,try塊捕捉的異常就不能拋出,外部捕捉到的異常就是finally塊中的異常資訊,而try塊中發生的真正的異常堆棧資訊則丟失了。
try、catch、finally語句塊的執行順序:
- 當try沒有發生異常時:try語句塊中的語句逐一被執行,程式將跳過catch語句塊,執行finally語句塊和其後的語句;
- 當try發生異常,catch語句塊裡沒有處理此異常的情況:此異常將會拋給JVM處理,finally語句塊裡的語句還是會被執行,但finally語句塊後的語句不會被執行;
- 當try發生異常,catch語句塊裡有處理此異常的情況:try語句塊中,出現異常之後的語句也不會被執行,匹配catch語句塊執行完後,執行finally語句塊裡的語句,最後執行finally語句塊後的語句;
引用一個例子:
public class TestException { public TestException() { } boolean testEx() throws Exception { boolean ret = true; try { ret = testEx1(); } catch (Exception e) { System.out.println("testEx, catch exception"); ret = false; throw e; } finally { System.out.println("testEx, finally; return value=" + ret); return ret; (finally block does not complete normally) } } boolean testEx1() throws Exception { boolean ret = true; try { ret = testEx2(); if (!ret) { return false; } System.out.println("testEx1, at the end of try"); return ret; } catch (Exception e) { System.out.println("testEx1, catch exception"); ret = false; throw e; } finally { System.out.println("testEx1, finally; return value=" + ret); return ret; (finally block does not complete normally) } } 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"); ret = false; throw e; } finally { System.out.println("testEx2, finally; return value=" + ret); return ret; (finally block does not complete normally) } } public static void main(String[] args) { TestException testException1 = new TestException(); try { testException1.testEx(); } catch (Exception e) { e.printStackTrace(); } } }
結果輸出是:
i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
當finall塊中包含return語句時,Eclipse會給出警告“finally block does not complete normally”,原因分析如下:
1、不管try塊、catch塊中是否有return語句,finally塊都會執行。
2、finally塊中的return語句會覆蓋前面的return語句(try塊、catch塊中的return語句),所以如果finally塊中有return語句,Eclipse編譯器會警示告“finally block does not complete normally”。
3、如果finally塊中包含了return語句,即使前面的catch塊重新拋出了異常,則調用該方法的語句也不會獲得catch塊重新拋出的異常,而是會得到finally塊的傳回值,並且不會捕獲異常。
結論,應避免在finally塊中包含return語句。如果你在前面的語句中包含了return語句或重新拋出了異常,又在finally塊中包含了return語句,說明你概念混淆,沒有理解finally塊的意義。
引用資料:
深入理解java異常處理機制
異常的深入研究與分析
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
深入理解Java異常