Java自定義異常與異常使用最佳實踐
阿新 • • 發佈:2019-01-02
異常的分類
1. 非執行時異常(Checked Exception)
Java中凡是繼承自Exception但不是繼承自RuntimeException的類都是非執行時異常。
2. 執行時異常(Runtime Exception/Unchecked Exception)
RuntimeException類直接繼承自Exception類,稱為執行時異常。Java中所有的執行時異常都直接或間接的繼承自RuntimeException。
Java中所有的異常類都直接或間接的繼承自Exception。
異常的處理
一、對應非執行時異常,必須對其進行處理。處理方式有兩種:
□使用try…catch…finally語句塊進行捕獲
□在產生異常的方法所在的方法宣告throws Exception
二、對於執行時異常,可以不對其進行處理,也可以對其進行處理。一般情況下都不對其進行處理。
在使用Java API的方法時會產生異常,由於實際的需要,我們需要建立和使用自定義異常。使用全新的異常類,應用到系統程式中。
在介紹自定義異常時,首要先談談什麼要使用自定義異常,使用自定義異常的好處。建立自定義異常是為了表示應用程式的一些錯誤型別,為程式碼可能發生的一個或多個問題提供新的含義;可以顯示程式碼多個位置之間的錯誤的相似處,也可區分程式碼執行時可能出現的相似問題的一個或多個錯誤,或給出應用程式中一組錯誤的特殊含義。
應用場景
伺服器的基本作用是處理與客戶機的通訊,若使用標準Java API(如java.io和java.net包中的類)來編寫伺服器,則可使編寫的程式碼在多個位置丟擲IOException。在設定伺服器、等待客戶機連線和獲取通訊流時,可丟擲IOException,在通訊期間及試圖斷開連線時,也會丟擲IOException。簡而言之,伺服器的各個部分都是引發IOException。
對於伺服器而言,這樣IOException意義不盡相同。雖然由同一異常型別表示,但與各個異常先關的業務含義存在差異,報告和恢復操作也不相同。所以,可以將一個異常集與伺服器配置和啟動問題關聯,將另一個異常集與客戶機通訊的實際行動關聯,將第三個異常集與伺服器關閉任務關聯。使用自定義異常,可採用對應用程式有意義的方式來靈活地表示錯誤。
為此,我們需要使用自定義異常來定為問題,定位問題與異常實際準確的位置。
自定義異常類過程
1. 多數情況下,只需要繼承異常類Exception, 經常需要定義一個或多個建構函式,以在物件中儲存錯誤訊息。
擴充套件:
□類java.lang.Throwable是所有異常類的基類,它包括兩個子類:Exception和Error,Exception類用於描述程式能夠捕獲的異常,如ClassNotFoundException。Error類用於指示合理的應用程式不應該試圖捕獲的嚴重問題,如虛擬機器錯誤VirtualMachineError
□自定義異常類可以繼承Throwable類或者Exception類,而不要繼承Error類。自定義異常類之間也可以有繼承關係
□需要為自定義異常類設計構造方法,以方便構造自定義異常物件。
在繼承任何異常時,將自動繼承Throwable類的一些標準特性,如:
□錯誤訊息
□棧跟蹤
□異常包裝
2. 宣告方法丟擲自定義異常。為了使用自定義異常,必須通知呼叫程式碼的類:要準備處理這個異常型別。為此,宣告一個或多個方法丟擲異常。找到異常發生點,新建異常並加上關鍵字throw。
3.自定義異常測試操作。
使用異常的最佳實踐
下面的部分我們列出了客戶端程式碼處理 API 丟擲異常的一些最佳實現方法。
1. 記得釋放資源
如果你正在用資料庫或網路連線的資源,要記得釋放它們。如果你使用的 API 僅僅使用 unchecked exception,你應該用完後釋放它們,使用 try-final。
DBUtil 是一個關閉連線的工具類。最重要的部分在於 finally,無論異常發不發生都會執行。在這個例子中,finally 關閉了連線,如果關閉過程中有問題發生的話,會丟擲一個 RuntimeException。
2. 不要使用異常作控制流程之用
生成棧回溯是非常昂貴的,棧回溯的價值是在於除錯。在流程控制中,棧回溯是應該避免的,因為客戶端僅僅想知道如何繼續。
下面的程式碼,一個自定義的異常 MaximumCountReachedException,用來控制流程。
useExceptionsForFlowControl()使用了一個無限的迴圈來遞增計數器,直至異常被丟擲。這樣寫不僅降低了程式碼的可讀性,也讓程式碼變得很慢。記住異常僅用在有異常發生的情況。
3. 不要忽略異常
當一個 API 方法丟擲 checked exception 時,它是要試圖告訴你你需要採取某些行動處理它。如果它對你來說沒什麼意義,不要猶豫,直接轉換成 unchecked exception 丟擲,千萬不要僅僅用空的{}catch 它,然後當沒事發生一樣忽略它。
4. 不要 catch 最高層次的 exception
Unchecked exception 是繼承自 RuntimeException 類的,而 RuntimeException 繼承自 Exception。如果 catch Exception 的話,你也會 catch RuntimeException。
上面的程式碼會忽略掉 unchecked exception。
5. 僅記錄 exception 一次
對同一個錯誤的棧回溯(stack trace)記錄多次的話,會讓程式設計師搞不清楚錯誤的原始來源。所以僅僅記錄一次就夠了。
總結:這裡是我總結出的一些異常處理最佳實施方法。我並不想引起關於 checked exception 和 unchecked exception 的激烈爭論。你可以根據你的需要來設計程式碼。我相信,隨著時間的推移,我們會找到些更好的異常處理的方法的。
1. 非執行時異常(Checked Exception)
Java中凡是繼承自Exception但不是繼承自RuntimeException的類都是非執行時異常。
2. 執行時異常(Runtime Exception/Unchecked Exception)
RuntimeException類直接繼承自Exception類,稱為執行時異常。Java中所有的執行時異常都直接或間接的繼承自RuntimeException。
Java中所有的異常類都直接或間接的繼承自Exception。
異常的處理
一、對應非執行時異常,必須對其進行處理。處理方式有兩種:
□使用try…catch…finally語句塊進行捕獲
□在產生異常的方法所在的方法宣告throws Exception
二、對於執行時異常,可以不對其進行處理,也可以對其進行處理。一般情況下都不對其進行處理。
在使用Java API的方法時會產生異常,由於實際的需要,我們需要建立和使用自定義異常。使用全新的異常類,應用到系統程式中。
在介紹自定義異常時,首要先談談什麼要使用自定義異常,使用自定義異常的好處。建立自定義異常是為了表示應用程式的一些錯誤型別,為程式碼可能發生的一個或多個問題提供新的含義;可以顯示程式碼多個位置之間的錯誤的相似處,也可區分程式碼執行時可能出現的相似問題的一個或多個錯誤,或給出應用程式中一組錯誤的特殊含義。
應用場景
伺服器的基本作用是處理與客戶機的通訊,若使用標準Java API(如java.io和java.net包中的類)來編寫伺服器,則可使編寫的程式碼在多個位置丟擲IOException。在設定伺服器、等待客戶機連線和獲取通訊流時,可丟擲IOException,在通訊期間及試圖斷開連線時,也會丟擲IOException。簡而言之,伺服器的各個部分都是引發IOException。
對於伺服器而言,這樣IOException意義不盡相同。雖然由同一異常型別表示,但與各個異常先關的業務含義存在差異,報告和恢復操作也不相同。所以,可以將一個異常集與伺服器配置和啟動問題關聯,將另一個異常集與客戶機通訊的實際行動關聯,將第三個異常集與伺服器關閉任務關聯。使用自定義異常,可採用對應用程式有意義的方式來靈活地表示錯誤。
為此,我們需要使用自定義異常來定為問題,定位問題與異常實際準確的位置。
自定義異常類過程
1. 多數情況下,只需要繼承異常類Exception, 經常需要定義一個或多個建構函式,以在物件中儲存錯誤訊息。
擴充套件:
□類java.lang.Throwable是所有異常類的基類,它包括兩個子類:Exception和Error,Exception類用於描述程式能夠捕獲的異常,如ClassNotFoundException。Error類用於指示合理的應用程式不應該試圖捕獲的嚴重問題,如虛擬機器錯誤VirtualMachineError
□自定義異常類可以繼承Throwable類或者Exception類,而不要繼承Error類。自定義異常類之間也可以有繼承關係
□需要為自定義異常類設計構造方法,以方便構造自定義異常物件。
在繼承任何異常時,將自動繼承Throwable類的一些標準特性,如:
□錯誤訊息
□棧跟蹤
□異常包裝
若要在異常中新增附加資訊,則可以為類新增一些變數和方法。本例演示的自定義異常沒有按照業務型別來命名,而是建立一個通用異常類,以retCd來區別發生異常的業務型別與發生位置,當然對於具體的retCd值,事先必須有具體的規定或說明。
/**
* 多數情況下,建立自定義異常需要繼承Exception,本例繼承Exception的子類RuntimeException
* @author Mahc
*
*/
public class CustomerException extends RuntimeException {
private String retCd ; //異常對應的返回碼
private String msgDes; //異常對應的描述資訊
public CustomerException() {
super();
}
public CustomerException(String message) {
super(message);
msgDes = message;
}
public CustomerException(String retCd, String msgDes) {
super();
this.retCd = retCd;
this.msgDes = msgDes;
}
public String getRetCd() {
return retCd;
}
public String getMsgDes() {
return msgDes;
}
}
2. 宣告方法丟擲自定義異常。為了使用自定義異常,必須通知呼叫程式碼的類:要準備處理這個異常型別。為此,宣告一個或多個方法丟擲異常。找到異常發生點,新建異常並加上關鍵字throw。
public class TestClass {
public void testException() throws CustomerException {
try {
<p> //..some code that throws <span style="font-family:SimSun;">CustomerException</span></p> } catch (Exception e) {
throw new CustomerException("14000001", "String[] strs's length < 4");
}
}
}
3.自定義異常測試操作。
public class TestCustomerException {
public static void main(String[] args) {
try {
TestClass testClass = new TestClass();
testClass.testException();
} catch (CustomerException e) {
e.printStackTrace();
System.out.println("MsgDes\t"+e.getMsgDes());
System.out.println("RetCd\t"+e.getRetCd());
}
}
}
以下的自定義異常的最佳實踐,摘自網路,經過參考擴充套件使用。使用異常的最佳實踐
下面的部分我們列出了客戶端程式碼處理 API 丟擲異常的一些最佳實現方法。
1. 記得釋放資源
如果你正在用資料庫或網路連線的資源,要記得釋放它們。如果你使用的 API 僅僅使用 unchecked exception,你應該用完後釋放它們,使用 try-final。
public void dataAccessCode (){
Connection conn = null;
try{
conn = getConnection ();
..some code that throws SQLException
}catch(SQLException ex){
ex.printStacktrace ();
} finally{
DBUtil.closeConnection (conn);
}
}
class DBUtil{
public static void closeConnection
(Connection conn){
try{
conn.close ();
} catch(SQLException ex){
logger.error ("Cannot close connection");
throw new RuntimeException (ex);
}
}
}
DBUtil 是一個關閉連線的工具類。最重要的部分在於 finally,無論異常發不發生都會執行。在這個例子中,finally 關閉了連線,如果關閉過程中有問題發生的話,會丟擲一個 RuntimeException。
2. 不要使用異常作控制流程之用
生成棧回溯是非常昂貴的,棧回溯的價值是在於除錯。在流程控制中,棧回溯是應該避免的,因為客戶端僅僅想知道如何繼續。
下面的程式碼,一個自定義的異常 MaximumCountReachedException,用來控制流程。
public void useExceptionsForFlowControl () {
try {
while (true) {
increaseCount ();
}
} catch (MaximumCountReachedException ex) {
}
//Continue execution }
public void increaseCount ()
throws MaximumCountReachedException {
if (count >= 5000)
throw new MaximumCountReachedException ();
}
useExceptionsForFlowControl()使用了一個無限的迴圈來遞增計數器,直至異常被丟擲。這樣寫不僅降低了程式碼的可讀性,也讓程式碼變得很慢。記住異常僅用在有異常發生的情況。
3. 不要忽略異常
當一個 API 方法丟擲 checked exception 時,它是要試圖告訴你你需要採取某些行動處理它。如果它對你來說沒什麼意義,不要猶豫,直接轉換成 unchecked exception 丟擲,千萬不要僅僅用空的{}catch 它,然後當沒事發生一樣忽略它。
4. 不要 catch 最高層次的 exception
Unchecked exception 是繼承自 RuntimeException 類的,而 RuntimeException 繼承自 Exception。如果 catch Exception 的話,你也會 catch RuntimeException。
try{
..
}catch(Exception ex){
}
上面的程式碼會忽略掉 unchecked exception。
5. 僅記錄 exception 一次
對同一個錯誤的棧回溯(stack trace)記錄多次的話,會讓程式設計師搞不清楚錯誤的原始來源。所以僅僅記錄一次就夠了。
總結:這裡是我總結出的一些異常處理最佳實施方法。我並不想引起關於 checked exception 和 unchecked exception 的激烈爭論。你可以根據你的需要來設計程式碼。我相信,隨著時間的推移,我們會找到些更好的異常處理的方法的。