異常(三):使用throws聲明拋出異常和使用throw拋出異常

1. 使用throws聲明拋出異常:java

使用throws聲明拋出異常的思路是,當前方法不知道如何處理這種類型的異常,該異常應該由上一級調用者處理;若是main方法也不知道如何處理這種類型的異常,也可使用throws聲明拋出異常,該異常將交給JVM處理。JVM對異常的處理方法是:打印異常的跟蹤棧信息,並終止程序運行,這就是前面程序在遇到異常後自動結束的緣由。程序員

throws聲明拋出異常只能在方法簽名中使用。若是某段代碼中調用了一個帶throws聲明的方法,該方法聲明拋出了Checked異常,則代表該方法但願它的調用者來處理該異常。也就是說,調用該方法時要麼放在try塊中顯式捕獲該異常,要麼放在另外一個帶throws聲明拋出的方法中。編程

使用throws聲明拋出異常時有一個限制:子類方法聲明拋出的異常類型應該是父類方法聲明拋出的異常類型的子類或相同,子類方法聲明拋出的異常不容許比父類方法聲明拋出的異常多。安全

因而可知,使用Checked異常至少存在以下兩大不便之處:spa

  • 對於程序中的Checked異常,Java要求必須顯式捕獲並處理該異常,或者顯式聲明拋出該異常,這就增長了編程複雜度。
  • 若是在方法中顯式聲明拋出Checked異常,將會致使方法簽名與異常耦合,若是該方法是重寫父類的方法,則該方法拋出的異常還會受到被重寫方法所拋出異常的限制。

在大部分狀況下,推薦使用Runtime異常,而不使用Checked異常。尤爲當程序須要自行拋出異常時,使用Runtime異常將更加簡潔。日誌

當使用Runtime異常時,程序無需在方法中聲明拋出Checked異常,一旦發生了自定義錯誤,程序只管拋出Runtime異常便可。code

若是程序須要在合適的地方捕獲異常並對異常進行處理,則同樣可使用try...catch塊來捕獲Runtime異常。對象

使用Runtime異常是比較省事的方式,使用這種方式既能夠享受「正常代碼和錯誤處理代碼分離」,「保證程序具備較好的健壯性」的優點,又能夠避免由於使用Checked異常帶來的編程繁瑣性。所以C#、Ruby、Python等語言沒有所謂的Checked異常,全部的異常都是Runtime異常。繼承

但Checked異常也有其優點Checked異常能在編譯時提醒程序員代碼可能存在的問題,提醒程序員必須注意處理該異常或者聲明該異常由該方法調用者來處理,從而能夠避免程序員由於粗心而忘記處理該異常的錯誤。字符串

2. 使用throw拋出異常:

當程序出現錯誤時,系統會自動拋出異常;除此以外,Java也容許程序自行拋出異常,自行拋出異常使用throw語句來完成。

2.1 拋出異常:

不少時候,系統是否要拋出異常,可能須要根據應用的業務需求來決定,若是程序中的數據、執行與既定的業務需求不符,這就是一種異常。因爲與業務需求不符而產生的異常,必須由程序員來決定拋出,系統沒法拋出這種異常。

若是須要在程序中自行拋出異常,則應使用throw語句,throw語句能夠單獨使用,throw語句拋出的不是異常類,而是一個異常實例,並且每次只能拋出一個異常實例。當Java運行時接收到用戶自行拋出的異常時,一樣會停止當前的執行流,跳到該異常對應的catch塊,由該catch塊來處理該異常。也就是說,不論是系統自動拋出的異常,仍是程序員手動拋出的異常,Java運行時環境對異常的處理沒有任何差異。

若是throw語句拋出的異常是Checked異常,則該throw語句要麼處於try塊裏,顯式捕獲該異常,要麼放在一個帶throws聲明拋出的方法中,即把該異常交給該方法的調用者處理;若是throw語句拋出的異常是Runtime異常,則該語句無須放在try塊裏,也無須放在帶throws聲明拋出的方法中,程序既能夠顯式使用try...catch來捕獲並處理該異常,也能夠徹底不理會該異常,把異常交給方法調用者處理。

自行拋出Runtime異常比自行拋出Checked異常的靈活性更好,一樣,拋出Checked異常則可讓編譯器提醒程序員必須處理該異常。

2.2 自定義異常類:

在一般狀況下,程序不多會自行拋出系統異常,由於異常的類名一般也包含了該異常的有用信息。因此在選擇拋出異常時,應該選擇合適的異常類,從而能夠明確地描述該異常的狀況。在這種狀況下,應用程序經常須要拋出自定義異常。

用戶自定義異常都應該繼承Exception基類,若是但願自定義Runtime異常,則應該繼承RuntimeException基類。定義異常類時一般須要提供兩個構造器:一個是無參數的構造器,另外一個是帶一個字符串參數的構造器,這個字符串將做爲該異常對象的描述信息(也就是異常對象的getMessage()方法的返回值)。

2.3 catch和throw同時使用:

前面介紹的異常處理方式有以下兩種:

  • 在出現異常的方法內捕獲並處理異常,該方法的調用者將不能再次捕獲該異常。
  • 在方法簽名中聲明拋出該異常,將該異常徹底交給方法調用者處理。

在實際應用中每每須要更復雜的處理方式---當一個異常出現時,單靠某個方法沒法徹底處理該異常,必須由幾個方法協做纔可徹底處理該異常。也就是說,在異常出現的當前方法中,程序只對異常進行部分處理,還有些處理須要在該方法的調用者中才能完成,因此應該再次拋出異常。讓該方法的調用者也能捕獲到異常

爲了實現這種經過多個方法協做處理同一個異常的情形,能夠在catch塊中結合throw語句來完成。這種catch和throw結合使用的狀況在大型企業級應用中很是有用。企業級應用對異常的處理一般分紅兩個部分

  • 應用後臺須要經過日誌來記錄異常發生的詳細狀況。
  • 應用還須要根據異常向應用使用者傳達某種提示。在這種狀況下,全部異常都須要兩個方法共同完成,也就是必須將catch和throw結合使用。

2.4 異常鏈:

對於真實的企業級應用而言,經常有嚴格的分層關係,層與層之間有很是清晰的劃分,上層功能的實現嚴格依賴於下層的API,也不會跨層訪問。

當業務邏輯層訪問持久層出現SQLException異常時,程序不該該把底層的SQLException異常傳到用戶界面,有以下兩個緣由:

  • 對於正經常使用戶而言,他們不想看到底層SQLException異常,SQLException異常對他們使用該系統沒有任何幫助。
  • 對於惡意用戶而言,將SQLException異常暴露出來不安全。

將底層的原始異常直接傳給用戶是一種不負責任的表現。一般的作法是:程序先捕獲原始異常,而後拋出一個新的業務異常,新的業務異常中包含了對用戶的提示信息,這種處理方式被稱爲異常轉譯

這種把捕獲一個異常而後接着拋出另外一個異常,並把原始異常信息保存下來是一種典型的鏈式處理,也被稱爲「異常鏈」。

在JDK1.4之前,程序員必須本身編寫代碼來保持原始異常信息。從JDK1.4之後,全部Throwable的子類在構造器中均可以接收一個cause對象做爲參數。這個cause就用來表示原始異常,這樣能夠把原始異常傳遞給新的異常,使得即便在當前位置建立並拋出了新的異常,你也能經過這個異常鏈追蹤到異常最初發生的位置。

從JDK1.4以後,Throwable基類已有了一個能夠接收Exception參數的方法:

public class SalException extends Exception{
	public SalException(){

	}

	public SalException(String msg){
		super(msg);
	}

	public SalException(Throwable t){
		super(t);
	}
}

相關文章
相關標籤/搜索