2025-05-23

checked異常的一個問題是,有時候不允許拋出這樣的異常。特別是,如果要覆蓋超類中聲明的方法,或者實現接口中聲明的方法,而那個方法沒有聲明任何checked異常,那麼新的實現也不能聲明checked異常。


因此必須預先處理異常,另外,可以將異常轉換為運行時異常,或者繞過它而不處理它。但是,應該這樣做嗎,這其中是否隱藏著錯誤?


相關文章推薦:Java三種常見異常及解決 如何更合理的利用Java中的異常拋出


問題


隻要看一個例子,問題就清楚瞭。假設有一個File對象的List,需要按它們的標準路徑以字典順序排序。所謂標準路徑,是指在解析別名、符號鏈接和/../及/./之後得到的完整絕對路徑。本地方法使用一個比較器,如清單1所示:


清單1.按標準路徑比較兩個文件  importjava.io.File;  importjava.io.IOException;  importjava.util.ArrayList;  importjava.util.Collections;  importjava.util.Comparator;   publicclassFileComparatorimplementsComparator<File>{   publicintcompare(Filef1,Filef2){  returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());  }   publicstaticvoidmain(String[]args){  ArrayList<File>files=newArrayList<File>();  for(Stringarg:args){  files.add(newFile(arg));  }  Collections.sort(files,newFileComparator());  for(Filef:files){  System.out.println(f);  }  }   } 不幸的是,該代碼不能通過編譯。問題在於,getCanonicalPath()方法拋出一個IOException,因為它需要訪問文件系統。通常,當使用checked異常時,可以使用以下兩種方法之一:


1.將出錯的代碼包裝在一個try塊中,並捕捉拋出的異常。
2.聲明包裝方法(本例為compare())也拋出IOException。


通常,至於選擇何種方法,取決於是否能在拋出異常時合理地處理異常。如果能,那麼使用try-catch塊。如果不能,那麼聲明包裝方法本身拋出異常。不幸的是,這兩種技巧對於本例都不管用。在compare()方法中無法合理地處理IOException。從技術上講,似乎可以做到—即返回0、1或-1,如清單2所示:


清單2.拋出異常時返回一個默認值  publicintcompare(Filef1,Filef2){  try{  returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());  }  catch(IOExceptionex){  return-1;  }  } 然而,這違反瞭compare()方法的約定,因為它不是一個穩定的結果。對於相同的對象,前後兩次調用可能產生不同的結果。如果使用這個比較器來排序,那麼意味著最終列表沒有被正確排序。所以現在試試第2個選項—聲明compare()拋出IOException:


publicintcompare(Filef1,Filef2)throwsIOException{  returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());  } 這也不能通過編譯。因為checked異常是方法簽名的一部分,在覆蓋方法時,不能增加checked異常,就像不能改變return類型一樣。那麼最後還剩下一個折中選項:在compare()中捕捉異常,將它轉換成運行時異常,然後拋出運行時異常,如清單3所示:


清單3.將checked異常轉換成運行時異常  publicintcompare(Filef1,Filef2){  try{  returnf1.getCanonicalPath().compareTo(f2.getCanonicalPath());  }  catch(IOExceptionex){  thrownewRuntimeException(ex);  }  } 不幸的是,雖然這樣可以通過編譯,但是這種方法也不管用,其原因較為微妙。Comparator接口定義一個合約(請參閱參考資料)。這個合約不允許該方法拋出運行時異常(防止因違反泛型類型安全而成為調用代碼中的bug)。使用這個比較器的方法合理地依靠它來比較兩個文件,而不拋出任何異常。它們沒有準備好處理compare()中意外出現的異常。


正是由於這個微妙的原因,讓運行時異常成為代碼要處理的外部狀況是一個壞主意。這樣隻是逃避問題,並沒有真正處理問題。不處理異常所帶來的不良後果仍然存在,包括毀壞數據和得到不正確的結果。


這樣便陷入瞭困境。既不能在compare()內真正有效地處理異常,又不能在compare()之外處理異常。還剩下什麼地方可以處理異常—System.exit()?惟一正確的辦法是完全避免這種困境。幸運的是,至少有兩種方法可以做到這一點。


 


 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *