<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-204  評論-149  文章-0  trackbacks-0

    唉,今天ibm面試看來是被鄙視了,問及異常不知道是要問CheckedException和UncheckedException
    thinking in java看的早忘了。
    看了好幾篇帖子,還是理解不夠深刻
    http://www.javaeye.com/topic/2038       主題:為什么 Java 中要使用 Checked Exceptions
    http://www.javaeye.com/topic/14082     主題:我對Checked Exceptions的看法 (續為什么java引入Checked Ex...
    http://www.javaeye.com/topic/10482     主題:我對Checked Exception觀點的變化
    http://www.javaeye.com/topic/72170     主題:J2EE項目異常處理
    http://www.ibm.com/developerworks/cn/java/j-jtp05254/index.html

    http://www.onjava.com/pub/a/onjava/2006/01/11/exception-handling-framework-for-j2ee.html?page=6&x-order=date



    http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=2

    本文是Exception處理的一篇不錯的文章,從Java Exception的概念介紹起,依次講解了Exception的類型(Checked/Unchecked),Exception處理的最佳實現:
    1. 選擇Checked還是Unchecked的幾個經典依據
    2. Exception的封裝問題
    3. 如無必要不要創建自己的Exception
    4. 不要用Exception來作流程控制
    5. 不要輕易的忽略捕獲的Exception
    6. 不要簡單地捕獲頂層的Exception
    原文地址:
    http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html


    關于異常處理的一個問題就是要對何時(when)和如何(how)使用它們做到了然于心。在本文中我將介紹一些關于異常處理的最佳實踐,同時我也會涉及到最近爭論十分激烈的checked Exception的使用問題。

    作為開發員,我們都希望能寫出解決問題并且是高質量的代碼。不幸的是,一些副作用(side effects)伴隨著異常在我們的代碼中慢慢滋生。無庸置疑,沒有人喜歡副作用(side effects),所以我們很快就用我們自己的方式來避免它,我曾經看到一些聰明的程序員用下面的方式來處理異常:

    public void consumeAndForgetAllExceptions(){
    try {
    ...some code that throws exceptions
    } catch (Exception ex){
    ex.printStacktrace();
    }
    }

    上邊的代碼有什么問題么?

    在回答以前讓我們想想怎樣才是正確的?是的,一旦程序碰到異常,它就該掛起程序而"做"點什么。那么上邊的代碼是這樣子的么?看吧,它隱瞞了什么?它把所有的"苦水"往肚里咽(在控制臺打印出異常信息),然后一切繼續,從表面上看就像什么都沒有發生過一樣......,很顯然,上邊代碼達到的效果并不是我們所期望的。

    后來又怎樣?

    public void someMethod() throws Exception{
    }

    上邊的代碼又有什么問題?

    很明顯,上邊的方法體是空的,它不實現任何的功能(沒有一句代碼),試問一個空方法體能拋出什么異常?當然Java并不阻止你這么干。最近,我也遇到類似的情景,方法聲明會拋出異常,但是代碼中并沒有任何"機會"來"展示"異常。當我問開發員為什么要這樣做的時候,他回答我說"我知道,它確實有點那個,但我以前就是這么干的并且它確實能為我工作。"

    在C++社區曾經花了數年實踐來實踐如何使用異常,關于此類的爭論在 java社區才剛剛開始。我曾經看到許多Java程序員針對使用異常的問題進行爭論。如果對于異常處理不當的話,異常可以大大減慢應用程序的執行速度,因為它將消耗內存和CPU來創建、拋出并捕獲異常。如果過分的依賴異常處理,代碼對易讀和易使用這兩方面產生影響,以至于會讓我們寫出上邊兩處"糟糕"代碼。

    異常原理

    大體上說,有三種不同的"情景"會導致異常的拋出:

    l 編程錯誤導致異常(Exception due Programming errors): 這種情景下,異常往往處于編程錯誤(如:NullPointerException 或者 IllegalArgumentException),這時異常一旦拋出,客戶端將變得無能為力。

    l 客戶端代碼錯誤導致異常(Exception due client code errors): 說白點就是客戶端試圖調用API不允許的操作。

    l 資源失敗導致異常(Exception due to resource failures): 如內存不足或網絡連接失敗導致出現異常等。這些異常的出現客戶端可以采取相應的措施來恢復應用程序的繼續運行。

    Java中異常的類型

    Java 中定義了兩類異常:

    l Checked exception: 這類異常都是Exception的子類

    l Unchecked exception: 這類異常都是RuntimeException的子類,雖然RuntimeException同樣也是Exception的子類,但是它們是特殊的,它們不能通過client code來試圖解決,所以稱為Unchecked exception

    舉個例子,下圖為NullPointerException的繼承關系:


    圖中,NullPointerException繼承自RuntimeException,所以它是Unchecked exception.

    以往我都是應用checked exception多于Unchecked exception,最近,在java社區激起了一場關于checked exception和使用它們的價值的爭論。這場爭論起源于JAVA是第一個擁有Checked exception的主流OO語言這樣一個事實,而C++和C#都是根本沒有Checked exception,它們所有的異常都是unchecked。

    一個checked exception強迫它的客戶端可以拋出并捕獲它,一旦客戶端不能有效地處理這些被拋出的異常就會給程序的執行帶來不期望的負擔。
    Checked exception還可能帶來封裝泄漏,看下面的代碼:

    public List getAllAccounts() throws
    FileNotFoundException, SQLException{
    ...
    }

    上邊的方法拋出兩個異常。客戶端必須顯示的對這兩種異常進行捕獲和處理即使是在完全不知道這種異常到底是因為文件還是數據庫操作引起的情況下。因此,此時的異常處理將導致一種方法和調用之間不合適的耦合。



    接下來我會給出幾種設計異常的最佳實踐 (Best Practises for Designing the API)

    1. 當要決定是采用checked exception還是Unchecked exception的時候,你要問自己一個問題,"如果這種異常一旦拋出,客戶端會做怎樣的補救?"
    如果客戶端可以通過其他的方法恢復異常,那么這種異常就是checked exception;如果客戶端對出現的這種異常無能為力,那么這種異常就是Unchecked exception;從使用上講,當異常出現的時候要做一些試圖恢復它的動作而不要僅僅的打印它的信息,總來的來說,看下表:

    Client's reaction when exception happens
    Exception type

    Client code cannot do anything
    Make it an unchecked exception

    Client code will take some useful recovery action based on information in exception
    Make it a checked exception


    此外,盡量使用unchecked exception來處理編程錯誤:因為unchecked exception不用使客戶端代碼顯示的處理它們,它們自己會在出現的地方掛起程序并打印出異常信息。Java API中提供了豐富的unchecked excetpion,譬如:NullPointerException , IllegalArgumentException 和 IllegalStateException等,因此我一般使用這些標準的異常類而不愿親自創建新的異常類,這樣使我的代碼易于理解并避免的過多的消耗內存。

    2. 保護封裝性(Preserve encapsulation)

    不要讓你要拋出的checked exception升級到較高的層次。例如,不要讓SQLException延伸到業務層。業務層并不需要(不關心?)SQLException。你有兩種方法來解決這種問題:

    l 轉變SQLException為另外一個checked exception,如果客戶端并不需要恢復這種異常的話;

    l 轉變SQLException為一個unchecked exception,如果客戶端對這種異常無能為力的話;

    多數情況下,客戶端代碼都是對SQLException無能為力的,因此你要毫不猶豫的把它轉變為一個unchecked exception,看看下邊的代碼:
    public void dataAccessCode(){
    try{
    ..some code that throws SQLException
    }catch(SQLException ex){
    ex.printStacktrace();
    }
    }


    上邊的catch塊緊緊打印異常信息而沒有任何的直接操作,這是情有可原的,因為對于SQLException你還奢望客戶端做些什么呢?(但是顯然這種就象什么事情都沒發生一樣的做法是不可取的)那么有沒有另外一種更加可行的方法呢?

    public void dataAccessCode(){
    try{
    ..some code that throws SQLException
    }catch(SQLException ex){
    throw new RuntimeException(ex);
    }
    }

    上邊的做法是把SQLException轉換為RuntimeException,一旦SQLException被拋出,那么程序將拋出RuntimeException,此時程序被掛起并返回客戶端異常信息。

    如果你有足夠的信心恢復它當SQLException被拋出的時候,那么你也可以把它轉換為一個有意義的checked exception, 但是我發現在大多時候拋出RuntimeException已經足夠用了。

    3. 不要創建沒有意義的異常(Try not to create new custom exceptions if they do not have useful information for client code.)

    看看下面的代碼有什么問題?

    public class DuplicateUsernameException
    extends Exception {}


    它除了有一個"意義明確"的名字以外沒有任何有用的信息了。不要忘記Exception跟其他的Java類一樣,客戶端可以調用其中的方法來得到更多的信息。

    我們可以為其添加一些必要的方法,如下:

    public class DuplicateUsernameException
    extends Exception {
    public DuplicateUsernameException
    (String username){....}
    public String requestedUsername(){...}
    public String[] availableNames(){...}
    }



    在新的代碼中有兩個有用的方法:reqeuestedUsername(),客戶但可以通過它得到請求的名稱;availableNames(),客戶端可以通過它得到一組有用的usernames。這樣客戶端在得到其返回的信息來明確自己的操作失敗的原因。但是如果你不想添加更多的信息,那么你可以拋出一個標準的Exception:

    throw new Exception("Username already taken");
    更甚的情況,如果你認為客戶端并不想用過多的操作而僅僅想看到異常信息,你可以拋出一個unchecked exception:

    throw new RuntimeException("Username already taken");

    另外,你可以提供一個方法來驗證該username是否被占用。

    很有必要再重申一下,checked exception應該讓客戶端從中得到豐富的信息。要想讓你的代碼更加易讀,請傾向于用unchecked excetpion來處理程序中的錯誤(Prefer unchecked exceptions for all programmatic errors)。

    4. Document exceptions.

    你可以通過Javadoc's @throws 標簽來說明(document)你的API中要拋出checked exception或者unchecked exception。然而,我更傾向于使用來單元測試來說明(document)異常。不管你采用哪中方式,你要讓客戶端代碼知道你的API中所要拋出的異常。這里有一個用單元測試來測試IndexOutOfBoundsException的例子:

    public void testIndexOutOfBoundsException() {
    ArrayList blankList = new ArrayList();
    try {
    blankList.get(10);
    fail("Should raise an IndexOutOfBoundsException");
    } catch (IndexOutOfBoundsException success) {}
    }



    上邊的代碼在請求blankList.get(10)的時候會拋出IndexOutOfBoundsException,如果沒有被拋出,將fail ("Should raise an IndexOutOfBoundsException")顯示說明該測試失敗。通過書寫測試異常的單元測試,你不但可以看到異常是怎樣的工作的,而且你可以讓你的代碼變得越來越健壯。


    下面作者將介紹界中使用異常的最佳實踐(Best Practices for Using Exceptions)
    1. 總是要做一些清理工作(Always clean up after yourself)

    如果你使用一些資源例如數據庫連接或者網絡連接,請記住要做一些清理工作(如關閉數據庫連接或者網絡連接),如果你的API拋出Unchecked exception,那么你要用try-finally來做必要的清理工作:
    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是一個工具類來關閉Connection.有必要的說的使用的finally的重要性是不管程序是否碰到異常,它都會被執行。在上邊的例子中,finally中關閉連接,如果在關閉連接的時候出現錯誤就拋出RuntimeException.



    2. 不要使用異常來控制流程(Never use exceptions for flow control)

    下邊代碼中,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()用一個無限循環來增加count直到拋出異常,這種做法并沒有說讓代碼不易讀,但是它是程序執行效率降低。

    記住,只在要會拋出異常的地方進行異常處理。



    3. 不要忽略異常

    當有異常被拋出的時候,如果你不想恢復它,那么你要毫不猶豫的將其轉換為unchecked exception,而不是用一個空的catch塊或者什么也不做來忽略它,以至于從表面來看象是什么也沒有發生一樣。



    4. 不要捕獲頂層的Exception

    unchecked exception都是RuntimeException的子類,RuntimeException又繼承Exception,因此,如果單純的捕獲Exception,那么你同樣也捕獲了RuntimeException,如下代碼:

    try{
    ..
    }catch(Exception ex){
    }
    一旦你寫出了上邊的代碼(注意catch塊是空的),它將忽略所有的異常,包括unchecked exception.

    5. Log exceptions just once

    Logging the same exception stack trace more than once can confuse the programmer examining the stack trace about the original source of exception. So just log it once.

    posted on 2009-06-18 23:36 Frank_Fang 閱讀(3278) 評論(1)  編輯  收藏 所屬分類: Java編程

    評論:
    # re: Java Exception小結 2009-06-23 21:36 | Frank_Fang
    從帖子一中截取的自認為比較有用的

    這是個很好的話題,我是開發應用系統的,更喜歡Unchecked Exception,但我知道對于JDBC/Hibernate這些底層類庫來說,Checked Exception也是必不可少的。

    抽象一點說,Checked Exception是一種好的設計,可以讓代碼更加健壯。但XP里也有一條,不要設計過度。

    對于應用系統來說,大多數Exception情況下,只需要中斷操作,提示用戶錯誤信息,很少有需要外部代碼捕捉甚至進行處理的,所以絕大多數Exception應該是UnChecked Exception (RuntimeException),捕捉到的底層系統錯誤也應該封裝成Unchecked Exception拋出。

    UncheckedException帶來的不僅是代碼簡潔,而且給代碼更大的復用性。

    -----------------------------------------------------------------------------------------
    ajoo>>
    http://ajoo.javaeye.com/
    我是異常的堅定支持者。對為了效率而放棄異常的行為嗤之以鼻。
    異常出現的幾率是很小的,所以根本沒有必要擔心效率。

    不過checkUser還是返回boolean順眼。
    畢竟,異常表示的是“異常”,不是用在正常邏輯之中。

    我的checked和nonchecked異常的標準:
    1。如果一個異常的出現意味著bug,debug好的系統不應該出現這個問題(比如很多輸入不符合要求什么的),那么就是runtime.
    2。如果一個異常的出現是合理的,并且用戶需要對這個異常進行恢復,那么就用checked exception。比如IOException, FileNotFoundException等等。
    3。一個比較一般性的異常,用戶拿到了也基本上沒有恢復的能力的異常,用runtime。這點上,感覺SQLException, RemoteException設計的不好。

    Anders的觀點我還是部分認同的。
    所謂的異常會累積,我想主要出現在分層次的系統中。
    比如一個經典的設計:
    ui層 - 業務層 - 持久層 - 物理層。

    物理層會throw SQLException,而這個exception明顯不應該被任何中間層所截獲處理,而應該一直拋到最上層。
    但是,如果你在持久層聲明了throws SQLException,你的使用jdbc的實現細節就暴露了。
    所以,最好的辦法是catch住,然后封裝成一個持久層的exception。同樣如果物理層還會拋FileNotFoundException, RemoteException等等,這些都是物理層的實現細節,都要在持久層封裝。
    如果持久層封裝的時候繼續用checked exception,基于同樣的道理上面一層還是要繼續封裝。

    這樣的層層封裝累不累呀?有必要嗎?
    ??????spring中這個部分因而也變成了一個特色,大部分包中的異常通常再封裝一次?????


    我想anders的另一個觀點在于:exception spec復雜化了接口簽名,復雜化了語言。很明顯的一點,引入了generics的話,exception spec就讓情況復雜很多。
    總而言之,在有得有失,利弊難說的時候,保持簡單也許是一個從長遠角度考慮的正確決定。加一個東西永遠比去掉一個東西容易。(當然,眼前確實可能會讓軟件不那么健壯了)

    -----------------------------------------------------------------------------------------
    ajoo>>
    更正一下,沒有看清楚robbin的上下文就胡亂評價了. 該死.

    我完全同意robbin的關于loginUser的設計方法.

    如果只有用戶存在不存在的簡單情況,自然用返回值多快好省.

    但是,當可能的錯誤很多的時候,比如:
    用戶不存在,
    用戶存在但是被封
    密碼錯誤

    等等等等, 自然是用exception更好.
    用exception的好處是錯誤信息是結構化的,并且錯誤是可以任意擴展的.

    比如,用戶被刪除了, 我甚至可以報告用戶被刪除了幾天.
    用戶被封了,我甚至可以報告用戶被哪個該死的斑竹封的,什么時候封的,封了幾天等等.
    這些,你拿返回值來玩?

    這個exception是否是runtime的倒是值得商討.
    runtime的缺點是無法強制客戶代碼處理.
    checked的缺點是如果加入了新的exception種類,客戶代碼必須馬上改動, 如果中間有很多層,就要層層上報,即使這個exception暫時可能不會出現.
    (比如系統封人制度還沒有啟動)

    各有利弊. 一般來說我會讓UserNotExistException之類的作為checked exception, 一些不那么重要的exception也許就可以作為runtime.

    ---------------------------------------------------------------------------------------
    robbin
    使用Checked Exception還是UnChecked Exception的原則,我的看法是根據需求而定。

    如果你希望強制你的類調用者來處理異常,那么就用Checked Exception;
    如果你不希望強制你的類調用者來處理異常,就用UnChecked。

    那么究竟強制還是不強制,權衡的依據在于從業務系統的邏輯規則來考慮,如果業務規則定義了調用者應該處理,那么就必須Checked,如果業務規則沒有定義,就應該用UnChecked。

    還是拿那個用戶登陸的例子來說,可能產生的異常有:

    IOException (例如讀取配置文件找不到)
    SQLException (例如連接數據庫錯誤)
    ClassNotFoundException(找不到數據庫驅動類)

    NoSuchUserException
    PasswordNotMatchException

    以上3個異常是和業務邏輯無關的系統容錯異常,所以應該轉換為RuntimeException,不強制類調用者來處理;而下面兩個異常是和業務邏輯相關的流程,從業務實現的角度來說,類調用者必須處理,所以要Checked,強迫調用者去處理。

    在這里將用戶驗證和密碼驗證轉化為方法返回值是一個非常糟糕的設計,不但不能夠有效的標示業務邏輯的各種流程,而且失去了強制類調用者去處理的安全保障。

    至于類調用者catch到NoSuchUserException和PasswordNotMatchException怎么處理,也要根據他自己具體的業務邏輯了。或者他有能力也應該處理,就自己處理掉了;或者他不關心這個異常,也不希望上面的類調用者關心,就轉化為RuntimeException;或者他希望上面的類調用者處理,而不是自己處理,就轉化為本層的異常繼續往上拋出來。


    -----------------------------------------------------------------------------------
    ajoo>>
    今天和workmate也有了關于異常的爭論.

    問題是這樣:

    我們需要實現一個根據一些規則驗證url輸入的正確性.

    我的workmate設計成這樣:

    bool validateUrl(HttpServletRequest);

    我說: 你只能判斷輸入是否正確, 卻不能說哪里出錯啊. 為什么不用exception呢? 不管有什么錯誤, 用exception表示出來, 什么信息都不會丟失.

    他說: exception不好. 因為exception不應該參與正常control flow.

    我說: 這是你自己的意見. 看你怎么定義control flow了.

    他說, 90%的人都這樣認為.

    我說, 返回bool失去了對錯誤的fine control.我們只知道是否錯了, 不知道錯在哪里.

    他說, 我們可能不一定需要fine control.

    然后爭論不休.

    最后, 我問他, 那么你怎么告訴別人到底出了什么錯呢?

    他說: 我可以提供一個getError()函數嘛.

    我說: 那么, 你的getError()是否要重新validate一遍輸入呢?

    他說:那... 我可以返回String

    String validateUrl(...);

    我說, 光string怎么夠呢? 萬一我需要在html上面顯式錯誤信息, 我需要格式化的, 如果你的error message有回車, 你還要換成&lt;br&gt;呢.

    他說, 不用擔心, 我會處理br的. (估計是自己把回車替換成&lt;br&gt;吧)

    唉, 我還沒敢說, 萬一我們需要對同一個錯誤生成不同的東西呢? 比如, 在數據庫里log東西啦; 在html上用各種不同顏色,字體顯式不同級別的錯誤; 給mainframe發送xml信息之類的.
    他肯定會說: 沒那么復雜的.


    我說: 如果你throw exception, adapt成bool是舉手之勞. 而如果你返回bool,則不可能轉換成throw exception.

    反正,這老哥本著一個:不能用exception來處理control flow的教條, 就是不同意用exception.

    沒辦法.


    ----------------------------------------------------------------------------------------
    ajoo>>
    無數人說異常不應該控制程序流程。
    誰仔細想過為什么嗎?只怕都是人云亦云罷了。

    知道,異常效率低嘛。可是程序流程不見得就是都要高效率的呀。
    要真是效率決定一切,java程序員都去要飯得了,連c++扇子們都知道什么2/8原則,你們這里拿效率這么個雞毛還當令箭耍什么勁?

    沒人什么地方都用異常。但是有些場合用異常就是方便,錯誤種類可擴展,錯誤信息詳細,錯誤處理結構化,可以集中處理錯誤,不復雜化函數接口等等等等,這些都是用異常表達部分程序邏輯的好處。

    你說不應該控制程序流程,好辦。給個替代品啊。

    Java代碼
    UserInfo login(String username, String pwd);  
    throws AuthencationException; 

    UserInfo login(String username, String pwd);
    throws AuthencationException;
    我這里如果login失敗,用異常清晰簡單。您給個其它的好用的解決方案先?

    也不知道哪個弱人腦子都不過就給出這么個教條來。scheme還有continuation呢,那可是超級異常,不是一樣用來處理程序流程?
    具體情況具體分析,哪個方案最好用就用哪個,哪來那么多教條?

    ----------------------------------------------------------------------------------------
    checked Exception只不過實現了一個自動文檔化的功能,再就是提醒調用者去處理這個錯誤。
    但是,為了這點好處把方法簽名和exception綁在一起,其實是個得不償失的錯誤。這會使接口無謂復雜化,還增加了代碼的耦合程度。
    舉個例子來說,比如有一個接口I 的某個方法F throws ExceptionA,它的一個實現類里面,F方法里實現里調用了一個類的方法,這個類的方法throws Exception B,這個時候,如果你有三種選擇
    1、把Exception B用catch 吃掉
    2、改變接口的聲明,這個顯然行不通
    3、catch之后拋出一個unchecked Exception,Exception B就這樣被強暴了,變成了一個unchecked Exception。
    可見,checked Exception 如果沒有unchecked Exception的幫忙,連最基本的任務都做不好,這個小小的例子,已經充分證明了checked Exception自身就不是一個完備的異常體系,只不過是當時設計者頭腦一發熱,做出了一個自以為漂亮的愚蠢設計。
    C#取消掉checked Exception,完全是合理的。

    ------------------------------------------------------------------------------------------
    robbin>>
    異常類層次設計的不好帶來的結果就是非常糟糕,例如JTA的異常類層次,例如EJB的異常類層次,但是也有設計的很好的,例如Spring DataAccessException類層次結構。

    用設計糟糕的異常類層次來否定異常這個事物,是極度缺乏說服力的,就好像有人用菜刀砍了人,你不能否定這把菜刀一樣。

    這個帖子這么長了,該討論的問題都討論清楚了,總結也總結過n遍了,所以我早就沒有興趣再跟帖了。

    實際上,這個討論中隱含兩個不斷糾纏的話題:

    1、checked ,還是unchecked異常?
    2、用自定義的方法調用返回code,還是用異常來表達不期望各種的事件流

    經過這么長的討論,我認為結論已經非常清楚:

    1、應該適用unchecked異常,也就是runtimeexception,這樣可以讓無法處理異常的應用代碼簡單忽略它,讓更上層次的代碼來處理

    2、應該適用異常來表達不期望的各種事件流

    事實上,你們去看一下現在Spring,Hibernate3都已經采用這種方式,特別是Spring的DataAccessException異常層次設計,是一個很好的例子。即使用RuntimeException來表達不期望的各種事件流。

    -----------------------------------------------------------------------------------------

    第一部分 選擇checked or unchecked

    這里需要對異常的理解。什么算異常?java的異常處理機制是用來干什么的?異常和錯誤有什么區別?

    異常機制就是java的錯誤處理機制!java中的異常意味著2點:第一,讓錯誤處理代碼更有條理。這使得
    正常代碼和錯誤處理代碼分離。第二,引入了context的概念,認為有些錯誤是可以被處理的。問題就出在這兒了。

    java的checked異常指的就是在當前context不能被處理的錯誤!

    這句話其實是對上面2點的總結。首先checked異常是一種錯誤,其次這種錯誤可以被處理(或修復)。

    checked異常就是可以被處理(修復)的錯誤,unchecked異常其實就是無法處理(修復)的錯誤。

    說到這兒,應該清楚了。別的語言沒有checked異常,就是說它們認為錯誤都無法被修復,至少在語言級
    不提供錯誤修復的支持。java的catch clause干的就是錯誤修復的事。

    我的理解是,用好java的異常,其實就是搞清楚什么時候該用checked異常。應該把unchecked異常當作
    缺省行為。unchecked異常的意思是:當我做這件事時,不可思議的情況發生了,我沒辦法正常工作下去!
    然后拋出一個unchecked異常,程序掛起。而checked異常的意思是:當我做這件事時,有意外情況發生,
    可以肯定的是,活是沒法干了,但是要不要掛起程序,我這個函數沒法做主,我只能匯報上級!

    其實,從上面的分析可以看出,java引入checked異常只是讓程序員多了一個選擇,它并不強迫你使用checked異常。

    如果你對什么時候應該使用checked異常感到迷惑,那么最簡單的辦法就是,不要使用checked異常!這里包括2個
    方面:

    第一,你自己不必創建新的異常類,也不必在你的代碼中拋出checked異常,錯誤發生后只管拋出unchecked異常;
    第二,對已有API的checked異常統統catch后轉為unchecked異常!

    使用unchecked異常是最省事的辦法。用這種方法也可以享受“正常代碼和錯誤處理代碼分離”的好處。因為我們在調用方法時,
    不用根據其返回值判斷是否有錯誤出現,只管調用,只管做正事就ok了。如果出現錯誤,程序自然會知道并掛起。這樣的效果是怎樣
    的呢?

    第一,我們的業務代碼很清晰,基本都是在處理業務問題,而沒有一大堆判斷是否有錯的冗余代碼。(想想看,如果沒有
    throw異常的機制,你只能通過函數的返回值來判斷錯誤,那么你在每個調用函數的地方都會有判斷代碼!)
    第二,我們的代碼假設一切正常,如果確實如此,那么它工作良好。但是一旦出現任何錯誤,程序就會掛起停止運行。當然,你可以查看
    日志找到錯誤信息。

    那么使用checked異常又是怎樣的呢?

    第一,你需要考慮更多的問題。首先在設計上就會更加復雜,其次就是代碼更加冗長。設計上復雜
    體現在以下方面:

    1 對異常(錯誤)的抽象和理解。你得知道什么情況才能算checked異常,使得上級確實能夠處理(修復)這種異常,并且讓整個程序
    從這種設計中確實得到好處。

    2 對整個自定義checked異常繼承體系的設計。正如那篇文章所說,總不能在一個方法后面拋出20個異常吧!設計自定義checked異常,
    就要考慮方法簽名問題,在合適的時候拋出合適的異常(不能一味的拋出最具體的異常,也不能一味拋出最抽象的異常)

    第二,業務代碼相比較使用unchecked的情況而言,不夠直接了當了。引入了throws簽名和catch clause,代碼里有很多catch,方法
    簽名也和異常綁定了。

    第三,有了更強的錯誤處理能力。如果發生了checked異常,我們有能力處理(修復)它。表現在不是任何錯誤都會導致程序掛起,出現了
    checked異常,程序可能照樣運行。整個程序更加健壯,而代價就是前面2條。


    第二部分 使用checked異常的最佳實踐

    現在假設有些錯誤我們確定為checked異常,那么我們針對這些checked異常要怎樣編碼才合理呢?

    1 不要用checked異常做流程控制。無論如何,checked異常也是一種錯誤。只是我們可以處理(修復)它而已。這種錯誤和普通業務
    流程還是有區別的,而且從效率上來說,用異常控制業務流程是不劃算的。其實這個問題有時候很難界定,因為checked異常“可以修復”,
    那么就是說修復后程序照常運行,這樣一來真的容易跟普通業務流程混淆不清。比如注冊用戶時用戶名已經存在的問題。這個時候我們要考慮,
    為什么要用checked異常?這和使用業務流程相比,給我帶來了什么好處?(注意checked異常可以修復,這是和unchecked異常本質的區別)
    照我的理解,checked異常應該是介于正常業務流程和unchecked異常(嚴重錯誤)之間的一種比較嚴重的錯誤。出現了這種錯誤,程序無法
    完成正常的功能是肯定的了,但我們可以通過其他方式彌補(甚至修復),總之不會讓程序掛起就是。其實這一點也是設計checked異常時要考慮
    的問題,也是代價之一吧。

    2 對checked異常的封裝。這里面包括2個問題:

    第一,如果要創建新的checked異常,盡量包含多一點信息,如果只是一條message,那么用Exception好了。當然,用Exception會
    失去異常的型別信息,讓客戶端無法判斷具體型別,從而無法針對特定異常進行處理。

    第二,不要讓你要拋出的checked exception升級到較高的層次。例如,不要讓SQLException延伸到業務層。這樣可以避免方法
    簽名后有太多的throws。在業務層將持久層的所有異常統統歸為業務層自定義的一種異常。

    3 客戶端調用含有throws的方法要注意:

    第一,不要忽略異常。既然是checked異常,catch clause里理應做些有用的事情——修復它!catch里為空白或者僅僅打印出錯信息都是
    不妥的!為空白就是假裝不知道甚至瞞天過海,但是,出來混遲早要還的,遲早會報unchecked異常并程序掛起!非典就是個例子。
    打印出錯信息也好不到哪里去,和空白相比除了多幾行信息沒啥區別。如果checked異常都被這么用,那真的不如當初都改成unchecked好了,
    大家都省事!

    第二,不要捕獲頂層的Exception。你這么干,就是在犯罪!因為unchecked異常也是一種Exception!你把所有異常都捕獲了——不是我
    不相信你的能力,你根本就不知道該如何處理!這樣做的直接的后果就是,你的程序一般來說是不會掛起了,但是出現錯誤的時候功能廢了,
    表面上卻看不出什么!當然,深究起來,這也不是什么罪大惡極,如果你在catch里打印了信息,這和上面那條的情況是差不多的。而這2條
    的共同點就是,沒有正確使用checked異常!費了那么大勁設計的checked異常就是給你們上級(客戶端)用的,結果你們不會用!真的
    不如用unchecked干脆利落了!

    上面的最佳實踐是引用前面回帖中那篇翻譯的文章,再加上自己的一些理解寫成。ajoo對“不要用異常處理業務流程”提出異議,我是無法辯駁的,畢竟水平不夠。但我想,對于很多如我這樣尚在初級階段的程序員們,把前輩的話奉為教條也無可厚非吧? 至少這樣會少犯錯誤吧,呵呵。

    以上觀點均是自己的理解,水平所限,請不吝指正。

      回復  更多評論
      
    主站蜘蛛池模板: 免费特级黄毛片在线成人观看| 亚洲综合校园春色| 永久免费看bbb| 免费无码VA一区二区三区| 男女猛烈xx00免费视频试看| 亚洲妓女综合网99| 久久精品亚洲一区二区| 亚洲国产一区视频| 四虎1515hm免费国产| 成人网站免费观看| 久久国产免费福利永久| 日本黄色动图免费在线观看| sss在线观看免费高清| 国产成人亚洲精品电影| 亚洲色偷偷偷综合网| 亚洲一卡2卡4卡5卡6卡在线99| 水蜜桃亚洲一二三四在线| 亚洲日韩小电影在线观看| 亚洲精品网站在线观看不卡无广告| 韩国免费三片在线视频| 国产一卡2卡3卡4卡2021免费观看| 亚洲免费在线视频| 日本不卡免费新一区二区三区| 免费看无码特级毛片| a级片在线免费看| 在线观看免费黄色网址| 中文字幕av免费专区| 中国国产高清免费av片| a高清免费毛片久久| 精品免费久久久久国产一区 | 理论秋霞在线看免费| 亚洲日韩av无码中文| 亚洲色少妇熟女11p| 亚洲色欲色欲www在线播放| 亚洲欧洲av综合色无码| 亚洲国产aⅴ成人精品无吗| 国产偷国产偷亚洲清高APP| 午夜亚洲国产精品福利| 一区二区三区精品高清视频免费在线播放| 看Aⅴ免费毛片手机播放| 阿v免费在线观看|