亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java異常處理的最佳實(shí)踐

mayaohua / 3379人閱讀

摘要:然而,我更傾向于使用來單元測試來文檔化異常。單元測試允許我在使用中查看異常,并且作為一個(gè)可以被執(zhí)行的文檔來使用。通過為異常編寫單元測試,你不僅可以記錄異常如何觸發(fā),還可以使你的代碼在經(jīng)過這些測試后更加健壯。

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

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

Exception 的封裝問題

如無必要不要?jiǎng)?chuàng)建自己的 Exception

不要用 Exception 來作流程控制

不要輕易的忽略捕獲的 Exception

不要簡單地捕獲頂層的 Exception

 Best Practices for Exception Handling 
By Gunjan Doshi 11/19/2003
原文鏈接:http://www.onjava.com/pub/a/o...

關(guān)于異常處理的問題之一就是要知道何時(shí)(when)和如何(how)使用它。在本文中我將介紹一些關(guān)于異常處理的最佳實(shí)踐,同時(shí)我也會(huì)總結(jié)最近關(guān)于 checked Exception 使用問題的一些爭論。

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

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

上邊的代碼有什么問題么?
一旦拋出異常,正常的程序執(zhí)行流程被暫停并且將控制交給catch塊,catch塊捕獲異常并且只是 suppresses it(在控制臺(tái)打印出異常信息),之后程序繼續(xù)執(zhí)行,從表面上看就像什么都沒有發(fā)生過一樣……

那下面的這種方式呢?

public void someMethod() throws Exception { }

他的方法體是空的,它不實(shí)現(xiàn)任何的功能(沒有一句代碼),空白方法怎么(how)會(huì)(can)拋出異常?JAVA并不阻止你這么做。最近,我也遇到類似的代碼,方法聲明中會(huì)拋出異常,但是沒有實(shí)際發(fā)生(generated)該異常的代碼。當(dāng)我問程序員為什么要這樣做,他回答說“我知道這樣會(huì)影響API,但我已經(jīng)習(xí)慣了這樣做而且它很有效?!?/p>

C++社區(qū)曾經(jīng)花了數(shù)年時(shí)間來決定(decide)如何使用異常,關(guān)于此類的爭論在 java社區(qū)才剛剛開始。我看到許多Java程序員艱難(struggle)的使用異常。如果沒有正確使用,異常會(huì)影響程序的性能,因?yàn)樗枰褂脙?nèi)存和CPU來創(chuàng)建,拋出以及捕獲異常。如果過分的依賴異常處理,會(huì)使得代碼難以閱讀,并使使用API的程序員感到沮喪,我們都知道這將會(huì)帶來代碼漏洞(hacks)和代碼異味(code smells),
客戶端代碼可以通過忽略異?;驋伋霎惓肀荛_這個(gè)問題,如前兩個(gè)示例所示。

異常的本質(zhì)

從廣義上講,有三種不同的情景會(huì)導(dǎo)致異常的拋出:

編程錯(cuò)誤導(dǎo)致的異常 (Exception due Programming errors):這一類的異常是因?yàn)榫幊体e(cuò)誤發(fā)生的,(如NullPointerExceptionIllegalArgumentException),客戶端通常無法對這些編程錯(cuò)誤采取任何措施。

客戶端代碼錯(cuò)誤導(dǎo)致異常(Exceptions due to client code errors):客戶端代碼試圖調(diào)用API不允許的操作,從而違反了合約。如果異常中提供了有用的信息,客戶端可以通過其采用一些替代方法。例如:當(dāng)解析格式不正確的XML文件時(shí)會(huì)拋出異常。該異常中包含導(dǎo)致問題發(fā)生的XML內(nèi)容的具體位置。客戶端可以通過這些信息采取恢復(fù)措施。

資源失效導(dǎo)致的異常(Exceptions due to resource failures):當(dāng)資源失效時(shí)發(fā)生的異常。如內(nèi)存不足或網(wǎng)絡(luò)連接失敗??蛻舳藢Y源失效的回應(yīng)是要根據(jù)上下文來決定的??蛻舳丝梢栽谝欢螘r(shí)間之后重試該操作,或是只記錄資源失效日志并停止應(yīng)用程序。

Java 異常類型

Java 定義了兩類異常:

檢查型異常 (Checked exceptions):從 Exception 類繼承的異常都是檢查型異常(checked exceptions),客戶端必須處理API拋出的這類異常,通過catch子句捕獲或是通過throws子句繼續(xù)拋出(forwarding it outward)。

非檢查型異常 (Unchecked exceptions):RuntimeException 也是 Exception 的子類,然而,從RuntimeException 繼承的所有異常都會(huì)得到特殊處理。客戶端代碼不需要專門處理這類異常,因此它們被稱為 Unchecked exceptions.
舉例來說,下圖為 NullPointerException 的繼承關(guān)系。

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

我見過大量使用 checked exceptions 只在極少數(shù)時(shí)候使用 Unchecked exceptions。最近,Java社區(qū)關(guān)于 checked exceptions 及其真正價(jià)值進(jìn)行了熱烈討論,爭論源于Java似乎是第一個(gè)帶有 checked exceptions 的主流OO語言,而C++和C#根本沒有 checked exception,它們所有的異常都是unchecked .

從低層拋出的 checked exception 強(qiáng)制要求調(diào)用方捕獲或是拋出該異常。一旦客戶端不能有效地處理這些被拋出的異常,API和客戶端之間的異常協(xié)議(checked exception contract)就會(huì)變成不必要的負(fù)擔(dān)??蛻舳说某绦騿T可以通過將異常抑制(suppressing)在一個(gè)空的catch塊中或是直接拋出它。從而又將這個(gè)負(fù)擔(dān)交給了客戶端的調(diào)用者。

Checked exception還被指責(zé)可能會(huì)破壞封裝,看下面的代碼:

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

getAllAccounts() 方法拋出了兩個(gè)檢查型異常。調(diào)用此方法的客戶端必須明確的處理這兩種具體的異常,即使它并不知道在 getAllAccounts() 中哪個(gè)文件或是數(shù)據(jù)庫調(diào)用失敗了,
或者沒有提供文件系統(tǒng)或數(shù)據(jù)庫邏輯的業(yè)務(wù),因此,這樣的異常處理導(dǎo)致方法和調(diào)用者之間不當(dāng)?shù)膹?qiáng)耦合(tight coupling)。

設(shè)計(jì)異常的最佳實(shí)踐 (Best Practises for Designing the API)

在討論了這些之后,現(xiàn)在讓我們來探討一下如何設(shè)計(jì)一個(gè)正確拋出異常的API。

1. 當(dāng)要決定是采用 checked exceptions 還是 unchecked exceptions 的時(shí)候,問自己這樣的一個(gè)問題,“如果這種異常一旦拋出,客戶端會(huì)進(jìn)行怎樣的處理?”

如果客戶端可以采取措施從異常中恢復(fù),那就選擇 checked exception 。如果客戶端不能采取有效的措施,就選擇 unchecked exceptions 。有效的措施是指從異常中恢復(fù)的措施,而不僅僅是記錄異常日志??偨Y(jié)一下:

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 來處理編程錯(cuò)誤:unchecked exception 的優(yōu)點(diǎn)在于不強(qiáng)制客戶端顯示的處理它,它會(huì)傳播(propagate)到任何你想捕獲它的地方,或者它會(huì)在出現(xiàn)的地方掛起程序并報(bào)告異常信息。Java API中提供了豐富的 unchecked excetpion,如:NullPointerException , IllegalArgumentExceptionIllegalStateException 等。我更傾向于使用JAVA提供的標(biāo)準(zhǔn)異常類而不愿創(chuàng)建新的異常類,這樣使我的代碼易于理解并避免過多的消耗內(nèi)存。

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

永遠(yuǎn)不要讓特定于實(shí)現(xiàn)的 checked exception 傳遞到更高層,比如,不要將數(shù)據(jù)訪問層的 SQLException 傳遞到業(yè)務(wù)層,業(yè)務(wù)層并不需要了解(不關(guān)心? ) SQLException ,你有兩種方法來解決這種問題:

如果需要客戶端代碼從異常中恢復(fù),則將 SQLException 轉(zhuǎn)換為另一個(gè) checked exception 。

如果客戶端代碼無法對其進(jìn)行處理,請將 SQLException 轉(zhuǎn)換為 unchecked exception 。

大多數(shù)情況下,客戶端代碼都是對 SQLException 無能為力的,不要猶豫,把它轉(zhuǎn)換為一個(gè) 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被拋出,catch塊就會(huì)拋出一個(gè)RuntimeException,當(dāng)前執(zhí)行的線程將會(huì)停止并報(bào)告該異常。
但是,該異常并沒有影響到我的業(yè)務(wù)邏輯模塊,它無需進(jìn)行異常處理,更何況它根本無法對SQLException進(jìn)行任何操作。如果我的catch塊需要根異常原因,可以使用從JDK1.4開始所有異常類中都有的getCause()方法。
如果你確信在SQLException被拋出時(shí)業(yè)務(wù)層可以執(zhí)行某些恢復(fù)操作,那么你可以將其轉(zhuǎn)換為一個(gè)更有意義的 unchecked exception 。但是我發(fā)現(xiàn)在大多時(shí)候拋出RuntimeException已經(jīng)足夠用了。

3. 當(dāng)無法提供更加有用信息時(shí),不要自定義異常 (Try not to create new custom exceptions if they do not have useful information for client code.)

以下代碼有什么問題?

public class DuplicateUsernameException
    extends Exception {}

它除了有一個(gè)“意義明確”(indicative exception)的名字以外,它沒有給客戶端代碼提供任何有用的信息。不要忘記 Exception 跟其他的Java類一樣,你可以添加你認(rèn)為客戶端代碼將調(diào)用的方法供客戶端調(diào)用,以獲得有用的信息。
我們可以為 DuplicateUsernameException 添加一些必要的方法,如下:

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

新版本提供了兩個(gè)有用的方法: requestedUsername(),它會(huì)返回請求的名稱。availableNames(),它會(huì)返回一組與請求類似的可用的usernames??蛻舳丝梢允褂眠@些方法來告知所請求的用戶名不可用,其他用戶名可用。但是如果你不準(zhǔn)備添加這些額外的信息,那么只需拋出一個(gè)標(biāo)準(zhǔn)的Exception:

throw new Exception("Username already taken");

如果你認(rèn)為客戶端代碼除了記錄已經(jīng)采用的用戶名之外不會(huì)進(jìn)行任何操作,那么最好拋出 unchecked exception :

throw new RuntimeException("Username already taken");

另外,你可以提供一個(gè)方法來驗(yàn)證該username是否被占用。

很有必要再重申一下,在客戶端API可以根據(jù)異常信息進(jìn)行某些操作的情況下,將使用 checked exception 。
處理程序中的錯(cuò)誤更傾向于用 unchecked excetpion (Prefer unchecked exceptions for all programmatic errors)。它們使你的代碼更具可讀性。

4. 文檔化異常 (Document exceptions)

你可以使用 Javadoc 的 @throws 標(biāo)簽來說明(document)你的API中要拋出 checked exception 或者 unchecked exception。然而,我更傾向于使用來單元測試來文檔化異常(document exception)。單元測試允許我在使用中查看異常,并且作為一個(gè)可以被執(zhí)行的文檔來使用。不管你采用哪種方式,你要讓客戶端代碼知道你的API中所要拋出的異常。這是一個(gè)用單元測試來測試IndexOutOfBoundsException的例子: 這里提供了IndexOutOfBoundsException的單元測試。

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

上面這段代碼在調(diào)用 blankList.get(10) 應(yīng)當(dāng)拋出 IndexOutOfBoundsException 。如果沒有拋出該異常,則會(huì)執(zhí)行 fail("Should raise an IndexOutOfBoundsException") 顯式的說明該測試失敗了。通過為異常編寫單元測試,你不僅可以記錄異常如何觸發(fā),還可以使你的代碼在經(jīng)過這些測試后更加健壯。

使用異常的最佳實(shí)踐 (Best Practices for Using Exceptions)

下一組最佳實(shí)踐展示了客戶端代碼應(yīng)如何處理拋出 checked exception 的API。

1. 總是要做一些清理工作 (Always clean up after yourself)

如果你在使用如數(shù)據(jù)庫連接或是網(wǎng)絡(luò)連接之類的資源,請記住要做一些清理工作 (如關(guān)閉數(shù)據(jù)庫連接或者網(wǎng)絡(luò)連接),如果你調(diào)用的API僅拋出 Unchecked exception ,你應(yīng)該在使用后用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 連接,這里的重點(diǎn)在于 finally 塊,不管程序是否碰到異常,它都會(huì)被執(zhí)行。在上邊的例子中,在 finally 中關(guān)閉連接,如果在關(guān)閉連接的時(shí)候出現(xiàn)錯(cuò)誤就拋出 RuntimeException 。

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

生成堆棧跟蹤 (stack trace) 的代價(jià)很昂貴,堆棧跟蹤的價(jià)值在于debug中使用。在一個(gè)流程控制中,堆棧跟蹤應(yīng)當(dāng)被忽視,因?yàn)榭蛻舳酥幌胫廊绾芜M(jìn)行。

在下面的代碼中,MaximumCountReachedException 被用來進(jìn)行流程控制:

public void useExceptionsForFlowControl() {
    try {
        while (true) {
            increaseCount();
        }
    } catch (MaximumCountReachedException ex) {
    }
    //Continue execution
}

public void increaseCount()
    throws MaximumCountReachedException {
    if (count >= 5000)
        throw new MaximumCountReachedException();
}

useExceptionsForFlowControl() 用一個(gè)無限循環(huán)來增加count直到拋出異常,這種方式使得代碼難以閱讀,而且影響代碼性能。只在要會(huì)拋出異常的地方進(jìn)行異常處理。

3. 不要忽略異常 (Do not suppress or ignore exceptions)

當(dāng)API中的方法拋出 checked exception 時(shí),它在提醒你應(yīng)當(dāng)采取一些措施。如果 checked exception 沒有任何意義,請毫不猶豫的將其轉(zhuǎn)化為 unchecked exception 再重新拋出。而不是用一個(gè)空的 catch 塊捕捉來忽略它,然后繼續(xù)執(zhí)行,以至于從表面來看仿佛什么也沒有發(fā)生一樣。

4. 不要捕獲頂層的Exception (Do not catch top-level exceptions)

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

try{
    // ...
}catch(Exception ex){
}

上邊的代碼(注意catch塊是空的)將忽略所有的異常,包括 unchecked exception .

5. 只記錄異常一次 (Log exceptions just once)

將相同的異常多次記入日志會(huì)使得檢查追蹤棧的開發(fā)人員感到困惑,不知道何處是報(bào)錯(cuò)的根源。所以只記錄一次。

Summary

These are some suggestions for exception-handling best practices. I have no intention of staring a religious war on checked exceptions vs. unchecked exceptions. You will have to customize the design and usage according to your requirements. I am confident that over time, we will find better ways to code with exceptions.

I would like to thank Bruce Eckel, Joshua Kerievsky, and Somik Raha for their support in writing this article.

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72621.html

相關(guān)文章

  • Java異常處理 10 個(gè)最佳實(shí)踐

    摘要:為可恢復(fù)的錯(cuò)誤使用檢查型異常,為編程錯(cuò)誤使用非檢查型錯(cuò)誤。檢查型異常保證你對錯(cuò)誤條件提供異常處理代碼,這是一種從語言到強(qiáng)制你編寫健壯的代碼的一種方式,但同時(shí)會(huì)引入大量雜亂的代碼并導(dǎo)致其不可讀。在編程中選擇檢查型異常還是運(yùn)行時(shí)異常。 異常處理是Java 開發(fā)中的一個(gè)重要部分。它是關(guān)乎每個(gè)應(yīng)用的一個(gè)非功能性需求,是為了處理任何錯(cuò)誤狀況,比如資源不可訪問,非法輸入,空輸入等等。Java提供了...

    Forelax 評論0 收藏0
  • Java 異常處理 9 個(gè)最佳實(shí)踐

    摘要:異常處理的個(gè)最佳實(shí)踐原文地址翻譯出處在中,異常處理是個(gè)很麻煩的事情。使用描述性消息拋出異常這個(gè)最佳實(shí)踐背后的想法與前兩個(gè)類似。當(dāng)你以錯(cuò)誤的格式提供時(shí),它將被類的構(gòu)造函數(shù)拋出。類提供了特殊的構(gòu)造函數(shù)方法,它接受一個(gè)作為參數(shù)。 Java 異常處理的 9 個(gè)最佳實(shí)踐 原文地址:https://dzone.com/articles/9-...翻譯出處:https://www.oschina.n...

    sihai 評論0 收藏0
  • 貓頭鷹深夜翻譯:JAVA異常處理最佳實(shí)踐

    摘要:無需檢查的異常也是的子類。從低層拋出的需檢查異常強(qiáng)制要求調(diào)用方捕獲或是拋出該異常。當(dāng)前執(zhí)行的線程將會(huì)停止并報(bào)告該異常。單元測試允許我在使用中查看異常,并且作為一個(gè)可以被執(zhí)行的文檔來使用。不要捕獲最高層異常繼承的異常同樣是的子類。 前言 異常處理的問題之一是知道何時(shí)以及如何去使用它。我會(huì)討論一些異常處理的最佳實(shí)踐,也會(huì)總結(jié)最近在異常處理上的一些爭論。 作為程序員,我們想要寫高質(zhì)量的能夠解...

    W_BinaryTree 評論0 收藏0
  • Java快速掃盲指南

    摘要:不相等的對象要具有不相等的哈希碼為了哈希表的操作效率,這一點(diǎn)很重要,但不是強(qiáng)制要求,最低要求是不相等的對象不能共用一個(gè)哈希碼。方法和方法協(xié)同工作,返回對象的哈希碼。這個(gè)哈希碼基于對象的身份生成,而不是對象的相等性。 本文面向 剛學(xué)完Java的新手們。這篇文章不講語法,而是一些除了語法必須了解的概念。 將要去面試的初級工程師們。查漏補(bǔ)缺,以免遭遇不測。 目前由于篇幅而被挪出本文的知識...

    Tony_Zby 評論0 收藏0
  • 淺析Java異常處理機(jī)制

    摘要:關(guān)于異常處理的文章已有相當(dāng)?shù)钠?,本文簡單總結(jié)了的異常處理機(jī)制,并結(jié)合代碼分析了一些異常處理的最佳實(shí)踐,對異常的性能開銷進(jìn)行了簡單分析。是程序正常運(yùn)行中,可以預(yù)料的意外情況,應(yīng)該被捕獲并進(jìn)行相應(yīng)處理。 關(guān)于異常處理的文章已有相當(dāng)?shù)钠?,本文簡單總結(jié)了Java的異常處理機(jī)制,并結(jié)合代碼分析了一些異常處理的最佳實(shí)踐,對異常的性能開銷進(jìn)行了簡單分析。博客另一篇文章《[譯]Java異常處理的最...

    NSFish 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<