一、分析
Java語言的異常處理機制可以確保程序的健壯性,提高
系統的開發效率,但是Java API提供的異常都是比較低級(這里的低級指的是“低級別的異常”),只有開發人員才能看的懂,才明白發生了什么問題。對於終端用戶來說,這些異常基本上是天書,與業務無關,是純計算機語言的描述。
這就需要我們對異常進行封裝了。
二、場景
異常封裝有三方面的優點:
1.提高系統的友好性
例如,打開一個文件,如果文件不存在,則會報FileNotFoundException異常,如果該方法的編寫不做任何處理,直接上拋上層,則會降低系統的友好性,代碼如下所示:
[java]
public static void doStuff()throws Exception{
InputStream is = new FileInputStream("無效文件.txt");
/*文件操作*/
}
此時doStuff方法的友好性極差:出現異常時(比如文件不存在),該方法直接把FileNotFoundException異常拋出到上層應用中(或者是終端用戶),而上層應用(或用戶)要么自己處理,要么接着拋出,最終的結果就是讓用戶對着“天書”式的文字發呆,用戶不知道這是什么問題,只是系統告訴他”哦,我出錯了,什么錯誤?你自己看着辦吧“。
解決辦法就是封裝異常,可以把系統的
閱讀者分為兩類:開發人員和用戶。開發人員查找問題,需要打印出堆棧信息,而用戶則需要了解具體的業務原因,比如文件太大,不能同時編寫文件等,代碼如下:
[java]
public static void doStuff2()throws MyBussinessException{
try{
InputStream is = new FileInputStream("無效文件.txt");
}catch(FileNotFoundException e){
//為了方便開發和維護人員而設置的異常信息
e.printStackTree();
//拋出業務異常
throw new MyBussinessException(e);
}
}
2.提高系統的可維護性
[java]
public void doStuff(){
try{
//do something
}catch(Exception e){
e.printStackTrace();
}
}
這是很多程序員容易犯的錯誤,拋出異常是吧?分類處理多麻煩,就寫一個catch塊來處理所有異常吧。而且還信誓旦旦的說”JVM會打印出棧中的錯誤信息“,雖然這沒有錯,但是該信息只有開發人員自己才看的懂,維護人員看見這段異常基本上無法處理,因為需要深入到代碼邏輯中去分析問題。
正確的做法是對異常進行分類處理,並進行封裝輸出,代碼如下:
[java]
public void doStuff(){
try{
//do something
}catch(FileNotFoundException e){
log.info("文件夾未找到,使用默認配置文件....");
}catch(SecurityException 3){
log.info("無權訪問,可能原因是....");
e.printStackTrace();
}
}
如此包裝后,維護人員看到這樣子的異常就有了初步的判斷,或者檢查配置,或者初始化環境,不需要直接到代碼層級去分析了。
3.解決Java異常機制自身的缺陷
Java中的異常一次只能拋出一個,比如,doStuff方法有兩個邏輯代碼片段,如果在第一個邏輯片段中拋出異常,則第二個邏輯片段就不執行了,也就無法拋出第二個異常了。那么如何才能一次拋出兩個異常呢?
其實,使用自行封裝的異常可以解決問題,代碼如下:
[java]
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異常只是一個異常容器,可以容納多個異常,但它本身並不代表任何異常含義,它所解決的是一次拋出多個異常的問題,具體調用如下:
[java]
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對象進行校驗,然后返回所有異常。
三、建議
在開發的過程中,根據具體的情況和需要,對異常進行封裝。