一、分析
Java語言的異常處理機制可以確定程式的健壯性,提高系統的開發效率,但是JavaAPI提供的異常都是比較低級(這裡的低級指的是“低級别的異常”),隻有開發人員才能看的懂,才明白發生了什麼問題。對于終端使用者來說,這些異常基本上是天書,與業務無關,是純計算機語言的描述。
這就需要我們對異常進行封裝了。
二、場景
異常封裝有三方面的優點:
1.提高系統的友好性
例如,打開一個檔案,如果檔案不存在,則會報FileNotFoundException異常,如果該方法的編寫不做任何處理,直接上抛上層,則會降低系統的友好性,代碼如下所示:
public static void doStuff()throws Exception{
InputStream is = new FileInputStream("無效檔案.txt");
/*檔案操作*/
}
此時doStuff方法的友好性極差:出現異常時(比如檔案不存在),該方法直接把FileNotFoundException異常抛出到上層應用中(或者是終端使用者),而上層應用(或使用者)要麼自己處理,要麼接着抛出,最終的結果就是讓使用者對着“天書”式的文字發呆,使用者不知道這是什麼問題,隻是系統告訴他”哦,我出錯了,什麼錯誤?你自己看着辦吧“。
解決辦法就是封裝異常,可以把系統的閱讀者分為兩類:開發人員和使用者。開發人員查找問題,需要列印出堆棧資訊,而使用者則需要了解具體的業務原因,比如檔案太大,不能同時編寫檔案等,代碼如下:
public static void doStuff2()throws MyBussinessException{
try{
InputStream is = new FileInputStream("無效檔案.txt");
}catch(FileNotFoundException e){
//為了友善開發和維護人員而設定的異常資訊
e.printStackTree();
//抛出業務異常
throw new MyBussinessException(e);
}
}
2.提高系統的可維護性
public void doStuff(){
try{
//do something
}catch(Exception e){
e.printStackTrace();
}
}
這是很多程式員容易犯的錯誤,抛出異常是吧?分類處理多麻煩,就寫一個catch塊來處理所有異常吧。而且還信誓旦旦的說”JVM會列印出棧中的錯誤資訊“,雖然這沒有錯,但是該資訊隻有開發人員自己才看的懂,維護人員看見這段異常基本上無法處理,因為需要深入到代碼邏輯中去分析問題。
正确的做法是對異常進行分類處理,并進行封裝輸出,代碼如下:
public void doStuff(){
try{
//do something
}catch(FileNotFoundException e){
log.info("檔案夾未找到,使用預設配置檔案....");
}catch(SecurityException 3){
log.info("無權通路,可能原因是....");
e.printStackTrace();
}
}
如此包裝後,維護人員看到這樣子的異常就有了初步的判斷,或者檢查配置,或者初始化環境,不需要直接到代碼層級去分析了。
3.解決Java異常機制自身的缺陷
Java中的異常一次隻能抛出一個,比如,doStuff方法有兩個邏輯代碼片段,如果在第一個邏輯片段中抛出異常,則第二個邏輯片段就不執行了,也就無法抛出第二個異常了。那麼如何才能一次抛出兩個異常呢?
其實,使用自行封裝的異常可以解決問題,代碼如下:
class MyException extends Exception{
//容納所有異常
private List<Throwable> causes = new ArrayList<Throwable>();
//構造函數,傳遞一個異常清單
public MyException(List<? extends Throwable> _causes){
cause.addAll(_causes);
}
//讀取所有的異常
public List<Throwable> getException(){
return causes;
}
}
MyException異常隻是一個異常容器,可以容納多個異常,但它本身并不代表任何異常含義,它所解決的是一次抛出多個異常的問題,具體調用如下:
public static void doStuff()throws MyException{
List<Throwable> list = new ArrayList<Throwable>();
//第一個邏輯片段
try{
//Do something
}catch(Exception e){
list.add(e);
}
//第二個邏輯片段
try{
//Do something
}catch(Exception e){
list.add(e);
}
//檢查是否有必要抛出異常
if(list.size() > 0){
throw new MyException(list);
}
}
這樣一來,doStuff方法的調用者就可以一次獲得多個異常,也能夠為使用者提供完整的例外情況說明。
那麼在什麼情況下,需要一個方法抛出多個異常呢?比如Web界面注冊時,展示層依次把User對象傳遞給邏輯層,Register方法需要對各個Field進行校驗并注冊,例如,使用者名不能重複,密碼必須符合密碼政策等,不要出現第一次送出系統提示”使用者名重複“,在修改在送出後,系統提示“密碼長度少6位”的情況,這種操作模式使用者體驗非常糟糕,最好的解決辦法就是封裝異常,建立異常容器,一次性地對User對象進行校驗,然後傳回所有異常。
三、建議
在開發的過程中,根據具體的情況和需要,對異常進行封裝。