異常 這個詞有 我對此感到意外 的意思。問題出現了,你也許不清楚該如何處理,但你的確知道不該該置之不理;你要停下來,看看是否是有別人或在別的地方,可以處理這個問題。只是在當前的環境中尚未足夠的信息來解決這個問題,因此就把這個問題提交到一個更高級別的環境中,在這裏將做出正確的決定。程序員
使用異常帶來的另外一個至關明顯的好處是,它每每可以下降錯誤處理代碼的複雜度。若是不使用異常,那麼就必須檢查特定的錯誤,並在程序中的許多地方去處理它。而若是使用異常,那就沒必要在方法調用處進行檢查,由於異常機制將保證可以捕獲這個錯誤。而且,只需在一個地方處理錯誤,即所謂的 異常處理程序 中。這種方式不只節省代碼,並且把 描述在正常執行過程當中作什麼事 的代碼和 出了問題怎麼辦 的代碼相分離。bash
異常情形 是指阻止當前方法或做用域繼續執行的問題。把異常情形與普通問題相區分很重要,所謂的普通問題是指,在當前環境下能獲得足夠的信息,總能處理這個錯誤。而對於異常情形,就不能繼續下去了,由於在 當前環境下 沒法得到必要的信息來解決問題。你所能作的就是從當前環境跳出,而且把問題提交給上一級環境。這就是拋出異常時所發生的事情。網絡
當拋出異常後,有幾件事會隨之發生。首先,同Java中的其餘對象的建立同樣,將使用 new 在堆上建立異常對象。而後,當前的執行路徑被終止,而且從當前環境中彈出對異常對象的引用。此時,異常處理機制接管程序,並開始尋找一個恰當的地方來繼續執行程序。這個恰當的地方就是異常處理程序。它的任務是將程序從錯誤狀態中回覆,以使程序能要麼換一種方式運行,要麼繼續運行下去。函數
與使用Java中的其餘對象同樣,咱們老是用 new 在堆上建立異常對象,這也伴隨着存儲空間的分配和構造器的調用。全部標準異常類都有兩個構造器:一個是默認構造器;另外一個是接收字符串做爲參數,以便能把相關信息放入異常對象的構造器:spa
throw new NullPointerException("t = null");
code
關鍵字 throw 將產生許多有趣的結果。在使用new建立了異常對象以後,此對象的引用將傳給throw。orm
此外,可以拋出任意類型的 Throwable 對象,它是異常類型的根類。一般,對於不一樣類型的錯誤,要拋出相應的異常。錯誤信息能夠保存在異常對象內部或者用異常類的名稱來暗示。上一層環境經過這些信息來決定如何處理異常。(一般,異常對象中僅有的信息就是異常類型,除此以外不包含任何有意義的內容。)對象
若是在方法內部拋出了異常(或者在方法內部調用的其餘方法拋出了異常),這個方法將在拋出異常的過程當中結束。要是不但願方法就此結束,能夠在方法內設置一個特殊的塊來捕獲異常。由於在這個塊裏 嘗試 各類方法調用,因此稱爲try塊。它是跟在try關鍵字以後的普通程序塊:繼承
try {
// Code that might generate exception
}
複製代碼
有了異常處理機制,能夠把全部動做都放在try塊,而後只需在一個地方就能夠捕獲全部的異常。接口
###異常處理程序 固然,拋出的異常必須在某處獲得處理。這個 地點 便是 異常處理程序,並且針對每一個要捕獲的異常,得準備相應功能的處理程序。異常處理程序緊跟在try塊以後,以關鍵字catch表示:
try {
// Code that might generate exception
}catch (Type1 id1){
//Handle exception of Type1
}catch (Type2 id2){
//Handle exception of Type2
}catch (Type3 id3){
//Handle exception of Type3
}
複製代碼
沒必要拘泥於Java中已有的異常類型。Java提供的異常體系不可能碰見全部的但願加以報告的錯誤,因此能夠本身定義異常類來表示程序中可能會遇到的特定問題。
要本身定義異常類,必須從已有的異常類繼承,最好是選擇意思相近的異常類繼承(不過這樣的異常類不容易找)。創建新的異常類型最簡單的方法就是讓編譯器爲你產生默認構造器,這是由於,對異常來講,最重要的部分就是類名,因此默認構造函數的異常類在大多數狀況下已經夠用了。因此這幾乎不用寫多少代碼:
public class SimpleException extends Exception {}
public class InheritingExceptions {
public void f() throws SimpleException {
System.out.println("Throw SimpleException form f()");
throw new SimpleException();
}
}
public class Main {
public static void main(String[] args) {
InheritingExceptions sed = new InheritingExceptions();
try {
sed.f();
} catch (SimpleException e) {
System.out.println("Caught it!");
// e.printStackTrace();
}
}
}
複製代碼
Java鼓勵人們把方法可能會拋出的異常告知使用此方法的客戶端程序員。這是種優雅的作法,它使得調用者能確切知道寫什麼樣的代碼能夠捕獲全部潛在的異常。固然,若是提供了源代碼,客戶端程序員能夠在源代碼中查找throw語句來獲知相關信息,然而程序庫一般並與不與源代碼一塊兒發佈。
爲了防止這樣的問題,Java提供了相應的語法,使你能以禮貌的方式告訴客戶端程序員某個方法必定會拋出的異常類型,而後客戶端程序員就能夠應用相應的處理。這就是 異常說明。
異常說明 使用了附加的關鍵字 throws, 後面接一個全部潛在異常類型的列表,因此方法定義可能看起來像這樣:
public void g() throws SimpleException, IOException {}
複製代碼
代碼必須與異常說明保持一致。若是方法裏的代碼產生了異常卻沒有進行處理,編譯器會發現這個問題並提醒你:要麼處理這個異常,要麼就在異常說明中代表此方法將產生異常。
能夠聲明方法將拋出異常,實際上卻不拋出。編譯器相信了這個聲明,並強制此方法的用戶像真的拋出異常那樣使用這個方法。這樣作的好處是:爲異常先佔個位子,之後能夠拋出這種異常而不用修改已有的代碼。在定義抽象類基類和接口時這種能力很重要,這樣派生類或接口實現就可以拋出這些預先聲明的異常。
不管異常是否被拋出, finally 子句總能執行。
對於沒有垃圾回收和析構函數自動調用機制的語言來講,finally很是重要。它能使程序員保證:不管try快裏發生什麼,內存總能獲得釋放。但Java有垃圾回收機制,因此內存再也不是問題。並且,Java也沒有析構函數可供調用。因此,Java就是要把除內存以外的資源恢復到他們的初始狀態時,要用到finally子句。這種須要清理的資源資源包括但不限於:已經打開的文件或網絡鏈接等等。
try語句在返回前,將其餘全部的操做執行完,保留好要返回的值,而後轉入執行finally中的語句,然後分爲如下三種狀況