J2EE專案異常處理

shwenwen發表於2013-03-05

1.JAVA異常處理
2.Checked 異常 還是 unChecked 異常?
3.設計一個新的異常類
4.如何記錄異常
5.J2EE專案中的異常處理

[@more@]
J2EE專案異常處理
為什麼要在J2EE專案中談異常處理呢?可能許多java初學者都想說:“異常處理不就是try….catch…finally嗎?這誰都會啊!”。筆者在初學java時也是這樣認為的。如何在一個多層的j2ee專案中定義相應的異常類?在專案中的每一層如何進行異常處理?異常何時被丟擲?異常何時被記錄?異常該怎麼記錄?何時需要把checked Exception轉化成unchecked Exception ,何時需要把unChecked Exception轉化成checked Exception?異常是否應該呈現到前端頁面?如何設計一個異常框架?本文將就這些問題進行探討。
1. JAVA異常處理
在程式導向式的程式語言中,我們可以透過返回值來確定方法是否正常執行。比如在一個c語言編寫的程式中,如果方法正確的執行則返回1.錯誤則返回0。在vb或delphi開發的應用程式中,出現錯誤時,我們就彈出一個訊息框給使用者。
透過方法的返回值我們並不能獲得錯誤的詳細資訊。可能因為方法由不同的程式設計師編寫,當同一類錯誤在不同的方法出現時,返回的結果和錯誤資訊並不一致。
所以java語言採取了一個統一的異常處理機制。
什麼是異常?執行時發生的可被捕獲和處理的錯誤。
在java語言中,Exception是所有異常的父類。任何異常都擴充套件於Exception類。Exception就相當於一個錯誤型別。如果要定義一個新的錯誤型別就擴充套件一個新的Exception子類。採用異常的好處還在於可以精確的定位到導致程式出錯的原始碼位置,並獲得詳細的錯誤資訊。
Java異常處理透過五個關鍵字來實現,try,catch,throw ,throws, finally。具體的異常處理結構由try….catch….finally塊來實現。try塊存放可能出現異常的java語句,catch用來捕獲發生的異常,並對異常進行處理。Finally塊用來清除程式中未釋放的資源。不管理try塊的程式碼如何返回,finally塊都總是被執行。
一個典型的異常處理程式碼
java 程式碼
  1. public String getPassword(String userId)throws DataAccessException{
  2. String sql = “select password from userinfo where userid=’”+userId +”’”;
  3. String password = null;
  4. Connection con = null;
  5. Statement s = null;
  6. ResultSet rs = null;
  7. try{
  8. con = getConnection();//獲得資料連線
  9. s = con.createStatement();
  10. rs = s.executeQuery(sql);
  11. while(rs.next()){
  12. password = rs.getString(1);
  13. }
  14. rs.close();
  15. s.close();
  16. }
  17. Catch(SqlException ex){
  18. throw new DataAccessException(ex);
  19. }
  20. finally{
  21. try{
  22. if(con != null){
  23. con.close();
  24. }
  25. }
  26. Catch(SQLException sqlEx){
  27. throw new DataAccessException(“關閉連線失敗!”,sqlEx);
  28. }
  29. }
  30. return password;
  31. }
可以看出Java的異常處理機制具有的優勢:
給錯誤進行了統一的分類,透過擴充套件Exception類或其子類來實現。從而避免了相同的錯誤可能在不同的方法中具有不同的錯誤資訊。在不同的方法中出現相同的錯誤時,只需要throw 相同的異常物件即可。
獲得更為詳細的錯誤資訊。透過異常類,可以給異常更為詳細,對使用者更為有用的錯誤資訊。以便於使用者進行跟蹤和除錯程式。
把正確的返回結果與錯誤資訊分離。降低了程式的複雜度。呼叫者無需要對返回結果進行更多的瞭解。
強制呼叫者進行異常處理,提高程式的質量。當一個方法宣告需要丟擲一個異常時,那麼呼叫者必須使用try….catch塊對異常進行處理。當然呼叫者也可以讓異常繼續往上一層丟擲。
2. Checked 異常 還是 unChecked 異常?
Java異常分為兩大類:checked 異常和unChecked 異常。所有繼承java.lang.Exception 的異常都屬於checked異常。所有繼承java.lang.RuntimeException的異常都屬於unChecked異常。
當一個方法去呼叫一個可能丟擲checked異常的方法,必須透過try…catch塊對異常進行捕獲進行處理或者重新丟擲。
我們看看Connection介面的createStatement()方法的宣告。
public Statement createStatement() throws SQLException;
SQLException是checked異常。當呼叫createStatement方法時,java強制呼叫者必須對SQLException進行捕獲處理。
java 程式碼
  1. public String getPassword(String userId){
  2. try{
  3. ……
  4. Statement s = con.createStatement();
  5. ……
  6. Catch(SQLException sqlEx){
  7. ……
  8. }
  9. ……
  10. }
或者
java 程式碼
  1. public String getPassword(String userId)throws SQLException{
  2. Statement s = con.createStatement();
  3. }
(當然,像Connection,Satement這些資源是需要及時關閉的,這裡僅是為了說明checked 異常必須強制呼叫者進行捕獲或繼續丟擲)
unChecked異常也稱為執行時異常,通常RuntimeException都表示使用者無法恢復的異常,如無法獲得資料庫連線,不能開啟檔案等。雖然使用者也可以像處理checked異常一樣捕獲unChecked異常。但是如果呼叫者並沒有去捕獲unChecked異常時,編譯器並不會強制你那麼做。
比如一個把字元轉換為整型數值的程式碼如下:
java 程式碼
  1. String str = “123”;
  2. int value = Integer.parseInt(str);
parseInt的方法簽名為:
java 程式碼
  1. public static int parseInt(String s) throws NumberFormatException
當傳入的引數不能轉換成相應的整數時,將會丟擲NumberFormatException。因為NumberFormatException擴充套件於RuntimeException,是unChecked異常。所以呼叫parseInt方法時無需要try…catch
因為java不強制呼叫者對unChecked異常進行捕獲或往上丟擲。所以程式設計師總是喜歡丟擲unChecked異常。或者當需要一個新的異常類時,總是習慣的從RuntimeException擴充套件。當你去呼叫它些方法時,如果沒有相應的catch塊,編譯器也總是讓你透過,同時你也根本無需要去了解這個方法倒底會丟擲什麼異常。看起來這似乎倒是一個很好的辦法,但是這樣做卻是遠離了java異常處理的真實意圖。並且對呼叫你這個類的程式設計師帶來誤導,因為呼叫者根本不知道需要在什麼情況下處理異常。而checked異常可以明確的告訴呼叫者,呼叫這個類需要處理什麼異常。如果呼叫者不去處理,編譯器都會提示並且是無法編譯透過的。當然怎麼處理是由呼叫者自己去決定的。
所以Java推薦人們在應用程式碼中應該使用checked異常。就像我們在上節提到運用異常的好外在於可以強制呼叫者必須對將會產生的異常進行處理。包括在《java Tutorial》等java官方文件中都把checked異常作為標準用法。
使用checked異常,應意味著有許多的try…catch在你的程式碼中。當在編寫和處理越來越多的try…catch塊之後,許多人終於開始懷疑checked異常倒底是否應該作為標準用法了。
甚至連大名鼎鼎的《thinking in java》的作者Bruce Eckel也改變了他曾經的想法。Bruce Eckel甚至主張把unChecked異常作為標準用法。並發表文章,以試驗checked異常是否應該從java中去掉。Bruce Eckel語:“當少量程式碼時,checked異常無疑是十分優雅的構思,並有助於避免了許多潛在的錯誤。但是經驗表明,對大量程式碼來說結果正好相反”
關於checked異常和unChecked異常的詳細討論可以參考
Alan Griffiths
《java Tutorial》
使用checked異常會帶來許多的問題。
checked異常導致了太多的try…catch 程式碼
可能有很多checked異常對開發人員來說是無法合理地進行處理的,比如SQLException。而開發人員卻不得不去進行try…catch。當開發人員對一個checked異常無法正確的處理時,通常是簡單的把異常列印出來或者是乾脆什麼也不幹。特別是對於新手來說,過多的checked異常讓他感到無所適從。
java 程式碼
  1. try{
  2. ……
  3. Statement s = con.createStatement();
  4. ……
  5. Catch(SQLException sqlEx){
  6. sqlEx.PrintStackTrace();
  7. }
  8. 或者
  9. try{
  10. ……
  11. Statement s = con.createStatement();
  12. ……
  13. Catch(SQLException sqlEx){
  14. //什麼也不幹
  15. }
checked異常導致了許多難以理解的程式碼產生
當開發人員必須去捕獲一個自己無法正確處理的checked異常,通常的是重新封裝成一個新的異常後再丟擲。這樣做並沒有為程式帶來任何好處。反而使程式碼晚難以理解。
就像我們使用JDBC程式碼那樣,需要處理非常多的try…catch.,真正有用的程式碼被包含在try…catch之內。使得理解這個方法變理困難起來
checked異常導致異常被不斷的封裝成另一個類異常後再丟擲
java 程式碼
  1. public void methodA()throws ExceptionA{
  2. …..
  3. throw new ExceptionA();
  4. }
  5. public void methodB()throws ExceptionB{
  6. try{
  7. methodA();
  8. ……
  9. }catch(ExceptionA ex){
  10. throw new ExceptionB(ex);
  11. }
  12. }
  13. Public void methodC()throws ExceptinC{
  14. try{
  15. methodB();
  16. }
  17. catch(ExceptionB ex){
  18. throw new ExceptionC(ex);
  19. }
  20. }
我們看到異常就這樣一層層無休止的被封裝和重新丟擲。
checked異常導致破壞介面方法
一個介面上的一個方法已被多個類使用,當為這個方法額外新增一個checked異常時,那麼所有呼叫此方法的程式碼都需要修改。
可見上面這些問題都是因為呼叫者無法正確的處理checked異常時而被迫去捕獲和處理,被迫封裝後再重新丟擲。這樣十分不方便,並不能帶來任何好處。在這種情況下通常使用unChecked異常。
chekced異常並不是無一是處,checked異常比傳統程式設計的錯誤返回值要好用得多。透過編譯器來確保正確的處理異常比透過返回值判斷要好得多。
如果一個異常是致命的,不可恢復的。或者呼叫者去捕獲它沒有任何益處,使用unChecked異常。
如果一個異常是可以恢復的,可以被呼叫者正確處理的,使用checked異常
在使用unChecked異常時,必須在在方法宣告中詳細的說明該方法可能會丟擲的unChekced異常。由呼叫者自己去決定是否捕獲unChecked異常
轉載:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/143526/viewspace-1060262/,如需轉載,請註明出處,否則將追究法律責任。

相關文章