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

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

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

    小石頭
    Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.
    posts - 91,comments - 22,trackbacks - 0
    原文:http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html

    作者:Gunjan Doshi 2003-11-19

    譯者注:本文算是一篇學(xué)習(xí)筆記,僅供學(xué)習(xí)參考使用,有不妥之處,還請指出。2003-12-04



    “本文是Exception處理的一篇不錯的文章,從Java Exception的概念介紹起,依次講解了Exception的類型(Checked/Unchecked),Exception處理的最佳實現(xiàn):

    1. 選擇Checked還是Unchecked的幾個經(jīng)典依據(jù)

    2. Exception的封裝問題

    3. 如無必要不要創(chuàng)建自己得Exception

    4. 不要用Exception來作流程控制

    5. 不要輕易的忽略捕獲的Exception

    6. 不要簡單地捕獲頂層的Exception”

    ——選自JAVADigest.Net對原文的介紹



    “JAVADigest.Net這個站點不知道大家是否經(jīng)常上,就像它的名字一樣,它讓我們更加有效的消化Java,或者它就像個中轉(zhuǎn)站一樣,至少對我是這樣的,有些好的可以說是非常經(jīng)典的技術(shù)文章,我都是通過它第一次獲得,更多的時候我是為了偷懶才上JAVADigest.Net,因為如果是近期比較經(jīng)典的文章,它上邊都有介紹文字和原文連接。”

    ——小插曲并非常榮幸地推薦JAVADigest.Net給你







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

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



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




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



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



    后來又怎樣?



    public void someMethod() throws Exception{
    }




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



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



    在C++社區(qū)曾經(jīng)花了數(shù)年實踐來實踐如何使用異常,關(guān)于此類的爭論在java社區(qū)才剛剛開始。我曾經(jīng)看到許多Java程序員針對使用異常的問題進(jìn)行爭論。如果對于異常處理不當(dāng)?shù)脑挘惓?梢源蟠鬁p慢應(yīng)用程序的執(zhí)行速度,因為它將消耗內(nèi)存和CPU來創(chuàng)建、拋出并捕獲異常。如果過分的依賴異常處理,代碼對易讀和易使用這兩方面產(chǎn)生影響,以至于會讓我們寫出上邊兩處“糟糕”代碼。



    異常原理



    大體上說,有三種不同的“情景”會導(dǎo)致異常的拋出:

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

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

    l 資源失敗導(dǎo)致異常(Exception due to resource failures): 如內(nèi)存不足或網(wǎng)絡(luò)連接失敗導(dǎo)致出現(xiàn)異常等。這些異常的出現(xiàn)客戶端可以采取相應(yīng)的措施來恢復(fù)應(yīng)用程序的繼續(xù)運行。



    Java中異常的類型

    Java 中定義了兩類異常:

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

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

    舉個例子,下圖為NullPointerException的繼承關(guān)系:


    Figure 1. Sample exception hierarchy



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



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



    一個checked exception強迫它的客戶端可以拋出并捕獲它,一旦客戶端不能有效地處理這些被拋出的異常就會給程序的執(zhí)行帶來不期望的負(fù)擔(dān)。



    Checked exception還可能帶來封裝泄漏,看下面的代碼:



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




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



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



    1. 當(dāng)要決定是采用checked exception還是Unchecked exception的時候,你要問自己一個問題,“如果這種異常一旦拋出,客戶端會做怎樣的補救?”

    [原文:When deciding on checked exceptions vs. unchecked exceptions, ask yourself, "What action can the client code take when the exception occurs?"]

    如果客戶端可以通過其他的方法恢復(fù)異常,那么這種異常就是checked exception;如果客戶端對出現(xiàn)的這種異常無能為力,那么這種異常就是Unchecked exception;從使用上講,當(dāng)異常出現(xiàn)的時候要做一些試圖恢復(fù)它的動作而不要僅僅的打印它的信息,總來的來說,看下表:

    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不用使客戶端代碼顯示的處理它們,它們自己會在出現(xiàn)的地方掛起程序并打印出異常信息。Java API中提供了豐富的unchecked excetpion,譬如:NullPointerException , IllegalArgumentException 和 IllegalStateException等,因此我一般使用這些標(biāo)準(zhǔn)的異常類而不愿親自創(chuàng)建新的異常類,這樣使我的代碼易于理解并避免的過多的消耗內(nèi)存。



    2. 保護(hù)封裝性(Preserve encapsulation)

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

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

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

    多數(shù)情況下,客戶端代碼都是對SQLException無能為力的,因此你要毫不猶豫的把它轉(zhuǎn)變?yōu)橐粋€unchecked exception,看看下邊的代碼:



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


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



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




    上邊的做法是把SQLException轉(zhuǎn)換為RuntimeException,一旦SQLException被拋出,那么程序?qū)伋鯮untimeException,此時程序被掛起并返回客戶端異常信息。



    如果你有足夠的信心恢復(fù)它當(dāng)SQLException被拋出的時候,那么你也可以把它轉(zhuǎn)換為一個有意義的checked exception, 但是我發(fā)現(xiàn)在大多時候拋出RuntimeException已經(jīng)足夠用了。



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

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

    public class DuplicateUsernameException
    extends Exception {}


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

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

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



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

    throw new Exception("Username already taken");



    更甚的情況,如果你認(rèn)為客戶端并不想用過多的操作而僅僅想看到異常信息,你可以拋出一個unchecked exception:

    throw new RuntimeException("Username already taken");



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



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



    4. Document exceptions.

    你可以通過Javadoc’s @throws 標(biāo)簽來說明(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)

    如果你使用一些資源例如數(shù)據(jù)庫連接或者網(wǎng)絡(luò)連接,請記住要做一些清理工作(如關(guān)閉數(shù)據(jù)庫連接或者網(wǎng)絡(luò)連接),如果你的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是一個工具類來關(guān)閉Connection.有必要的說的使用的finally的重要性是不管程序是否碰到異常,它都會被執(zhí)行。在上邊的例子中,finally中關(guān)閉連接,如果在關(guān)閉連接的時候出現(xiàn)錯誤就拋出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()用一個無限循環(huán)來增加count直到拋出異常,這種做法并沒有說讓代碼不易讀,但是它是程序執(zhí)行效率降低。

    記住,只在要會拋出異常的地方進(jìn)行異常處理。



    3. 不要忽略異常

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



    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.



    總結(jié)

    這里給出了一些關(guān)于異常處理的一些最佳實踐,我并不想開始另一輪的關(guān)于checked exception 和 unchecked exception的爭論。你可以根據(jù)自己的實際情況定制自己異常處理,我堅信我們將有更好的辦法來處理我們代碼中的異常。



    在此,我將感謝Bruce Eckel, Joshua Kerievsky, 和Somik Raha對于寫這篇文章所給于我的支持。

    參考資源:

    Related Resources
    "Does Java need Checked Exceptions?" by Bruce Eckel
    "Exceptional Java," by Alan Griffiths
    "The trouble with checked exceptions: A conversation with Anders Hejlsberg, Part II" on Artima.com
    "Checked exceptions are of dubious value," on C2.com
    Conversation with James Gosling by Bill Venners
    關(guān)于作者:

    Gunjan Doshi works with agile methodologies and its practices and is a Sun certified Java programmer.

    詳細(xì)討論見:http://forum.javaeye.com/viewtopic.php?t=2038? 受益匪淺
    posted on 2007-01-11 19:05 小石頭 閱讀(233) 評論(0)  編輯  收藏 所屬分類: 轉(zhuǎn)載區(qū)
    主站蜘蛛池模板: 亚洲天堂免费在线| a级毛片免费观看网站| 18女人水真多免费高清毛片| 久久久久亚洲AV综合波多野结衣 | 99久久婷婷国产综合亚洲| 一级毛片不卡片免费观看| 亚洲AV日韩AV永久无码下载 | 水蜜桃视频在线观看免费播放高清| 亚洲国产精品尤物YW在线观看 | 农村寡妇一级毛片免费看视频| 免费国内精品久久久久影院| 无套内射无矿码免费看黄| 亚洲精品无码99在线观看| 久久免费视频一区| 久久99国产亚洲精品观看| 一级毛片**不卡免费播| 67194在线午夜亚洲| 青青青国产免费一夜七次郎| 污污的视频在线免费观看| 伊人久久综在合线亚洲91| 少妇太爽了在线观看免费视频| 亚洲精品美女视频| 男女交性永久免费视频播放 | 老汉色老汉首页a亚洲| 成人女人A级毛片免费软件| 亚洲AV噜噜一区二区三区| 久久亚洲av无码精品浪潮| 玖玖在线免费视频| va天堂va亚洲va影视中文字幕| 又爽又高潮的BB视频免费看| 中国人免费观看高清在线观看二区| 久久久久亚洲AV成人无码网站 | 一级毛片正片免费视频手机看| 亚洲国产精品嫩草影院在线观看| 久久午夜夜伦鲁鲁片免费无码影视| 亚洲人成人伊人成综合网无码 | 成年丰满熟妇午夜免费视频| 一级做受视频免费是看美女| 亚洲精品91在线| mm1313亚洲国产精品美女| 99在线观看视频免费|