Java筆記(三)異常
異常
一、概念
一)為什麼會有Java異常機制
在沒有Java異常機制的情況下,唯一的退出機制就是返回值,判斷是否異常的方法就是
返回值。方法根據是否異常返回不同的返回值,呼叫者根據不同的返回值進行判斷。每一
層方法都需要對呼叫的方法的不同返回值進行檢查和處理,程式的正常邏輯和異常邏輯混雜
到一起,使程式碼難以閱讀和維護。另外,異常畢竟是少數,程式設計師因此常常偷懶,忽略對異常
返回值的檢查,降低了程式的可靠性。
在有了異常機制後,程式的正常邏輯與異常邏輯就可以分離,異常情況就可以集中處理,異常還可以
自動向上傳遞,不再需要每層方法都進行處理,異常可不可能被自動忽略,從而處理異常的程式碼大大
減少,程式碼的可讀性,可維護性,可靠性都得到了提高。
二)Java異常處理機制
Java異常的預設處理機制:列印異常棧到螢幕,並退出程式。
異常處理機制會從當前函式查詢誰捕獲了該異常,如果這一層函式沒有捕獲就依次向
上一層函式查詢,直到主函式,如果主函式也沒有捕獲該異常,就使用預設機制,列印
異常棧資訊,並退出程式。
異常是相對於return的一種退出程式的機制可以由throw語句觸發,也可以由系統觸發。
三)異常的分類
異常可以分類為:
1)受檢(checked)異常:強制要求程式設計師處理(捕獲或者通過throws關鍵字宣告)的異常,否則編譯器會報錯。
2)未受檢(unchecked)異常:不要求程式設計師處理。
關於受檢異常和未受檢異常,以前的說法是:
未受檢異常屬於程式設計的邏輯錯誤,程式設計時應該檢查以避免這些錯誤,比如空指標異常,
如果真的出現這種錯誤,程式退出也是正常的,程式設計師應該檢查程式碼BUG而不是想辦法
處理這種異常。受檢異常表示程式本身沒問題,但由於I/O、網路、資料庫等不可預測的
錯誤導致的異常,呼叫者應該處理。
但實際上程式設計錯誤也是應該處理的,尤其Java被廣泛應用於伺服器應用中,不能
因為一個邏輯錯誤就使程式退出。所以,目前一種更被認同的的觀點是:Java中
對受檢異常和未受檢異常的區分是沒有太大意義的,可以統一使用未受檢異常來代替。
這種觀點的基本理由是:無論受檢異常還是未受檢異常,無論是否出現在throws宣告中
,都應該在合適的地方以適當的方式進行處理,而不只是為了滿足編譯器的要求盲目處理
異常,既然都要進行處理異常,受檢異常的強制宣告和處理就顯得很繁瑣,尤其在呼叫層次比較深的情況下。
異常的類體系:
1.Throwable
有四個構造方法:
1. public Throwable() 2. public Throwable(String message) 3. public Throwable(String message, Throwable cause) 4. public Throwable(Throwable cause)
其中message表示異常訊息,cause表示觸發該異常的其他異常。異常可以形成一個異常鏈,
上層的異常由底層觸發,cause表示的是底層異常。Throwable還有一個public方法用於設定cause:
Throwable initCause(Throwable cause)
Throwable某些子類沒有建構函式就可以通過該方法設定。這個方法最多隻能被呼叫一次。
2.Error
Error(unchecked):描述了Java執行時系統內部錯誤和資源耗盡錯誤。
應用程式不應該丟擲此類異常。如果出現這樣的內部錯誤,應該通報給客戶,並盡力使程式安全退出。
3.Exception
Exception層次:
RuntimeException(unchecked):由於程式錯誤(程式碼有問題)導致的異常,如
錯誤的型別轉換,
陣列訪問越界,
訪問空指標
這類異常一定是你的錯誤。
其他異常(checked):程式本身沒有問題,由於像I/O錯誤這類問題導致的異常。如:
試圖在檔案末尾後面讀取資料
試圖開啟一個不存在的檔案。
編譯器將核查是否所有checked異常提供了異常處理器。
四)異常的處理
重新丟擲:因為當前層級不能處理該異常,丟擲給呼叫者處理。
丟擲一個新異常:當前異常所包含的資訊不夠,或者太過詳細。
throws關鍵字:用於宣告一個方法可能丟擲的異常,這個宣告的
含義是這個方法可能丟擲這些異常,並且沒有對這些異常進行處理,呼叫者必須進行處理。
finally執行細節:
1)如果在try或者catch語句內有return語句,則return語句在finally語句
執行結束後才執行,但finally並不能改變返回值。
//返回0 public static int test(){ int ret = 0; try{return ret; }finally{ ret = 2; } }
2)如果在finally語句中也有return語句:try和catch中的return會丟失,實際返回finally中的返回值。
finally中有return還會掩蓋try和catch內的異常,就像沒有異常發生一樣。如果finally中丟擲了異常,
則原異常也會被覆蓋。
public static int test(){ int ret = 0; try{ int a = 5/0; return ret; }finally{ //返回2而不向上傳遞異常 return 2; } }
public static void test(){ try{ int a = 5/0; }finally{ //原來異常ArithmeticException丟失 throw new RuntimeException("hello"); } }
因此:應該避免在finally中return或者丟擲異常,如果呼叫的其他程式碼可能丟擲異常,
則應該捕獲異常並進行處理。