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

資訊專欄INFORMATION COLUMN

淺析Java異常處理機(jī)制

NSFish / 2470人閱讀

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

關(guān)于異常處理的文章已有相當(dāng)?shù)钠?,本文?jiǎn)單總結(jié)了Java的異常處理機(jī)制,并結(jié)合代碼分析了一些異常處理的最佳實(shí)踐,對(duì)異常的性能開銷進(jìn)行了簡(jiǎn)單分析。
博客另一篇文章《[譯]Java異常處理的最佳實(shí)踐》也是關(guān)于異常處理的一篇不錯(cuò)的文章。

請(qǐng)思考: 對(duì)比 ExceptionError ,二者有何區(qū)別? 另外,運(yùn)行時(shí)異常和一般異常有什么區(qū)別?

Exception 和 Error 的區(qū)別

首先,要明確的是 ExceptionError 都繼承自 Throwable 類,Java中只有 Throwable 類型的實(shí)例才可以被拋出 (throws) 或 捕獲 (catch) ,它是異常處理機(jī)制的基本組成類型。

Exception 是程序正常運(yùn)行中,可以預(yù)料的意外情況,應(yīng)該被捕獲并進(jìn)行相應(yīng)處理。
Error 是指在正常情況下,不太可能出現(xiàn)的情況,絕大多數(shù)的 Error 都會(huì)導(dǎo)致程序(比如 JVM 自身)處于非正常的、不可恢復(fù)狀態(tài)。既然是非正常情況,所以不便于也不需要捕獲,常見的如 OutOfMemoryError等,都是 Error 的子類。

Exception 又分為檢查型 (checked) 和 非檢查型 (unchecked) 異常,檢查型異常必須在源代碼里顯式的進(jìn)行捕獲處理,這是編譯期檢查的一部分。

非檢查型異常(unchecked exception) 就是所謂的運(yùn)行時(shí)異常,如 NullPointerExceptionArrayIndexOutOfBoundsException 等,通常是可以編碼避免的邏輯錯(cuò)誤,具體根據(jù)需要來(lái)判斷是否需要捕獲,并不會(huì)在編譯期強(qiáng)制要求。

Throwable、Exception、Error 的設(shè)計(jì)和分類 內(nèi)置異常類

下圖展示了Java中異常類繼承關(guān)系

java.lang 中定義了一些異常類,這里只列舉其中常見的一部分,詳細(xì)查閱 java.lang.Error、java.lang.Exception

Error:

LinkageError:

VirtualMachineError:虛擬機(jī)錯(cuò)誤。用于指示虛擬機(jī)被破壞或者繼續(xù)執(zhí)行操作所需的資源不足的情況。

OutOfMemoryError: 內(nèi)存溢出錯(cuò)誤

StackOverflowError:棧溢出錯(cuò)誤

Exception

檢查型異常 (checked exception)

IOException

ClassNotFoundException

InstantiationException

SQLException

非檢查型異常 (unchecked exception)

RuntimeException

NullPointerException

ClassCastException

SecurityException

ArithmeticException

IndexOutOfBoundsException

還有一個(gè)經(jīng)典的題目: NoClassDefFoundErrorClassNotFoundException 有什么區(qū)別?

異常方法

下面是 Throwable 類的主要方法:(java.lang.Throwable)

public String getMessage() :返回關(guān)于發(fā)生的異常的詳細(xì)信息

public Throwable getCause():返回一個(gè)Throwable 對(duì)象代表異常原因

public void printStackTrace():打印toString()結(jié)果和棧層次到System.err,即錯(cuò)誤輸出流。

public String toString():Returns a short description of this throwable.

捕獲、拋出異常 try-catch-finally

使用trycatch 關(guān)鍵字可以捕獲異常。
可以在 try 語(yǔ)句后面添加任意數(shù)量的 catch 塊來(lái)捕獲不同的異常。如果保護(hù)代碼中發(fā)生異常,異常被拋給第一個(gè) catch 塊,如果匹配,它在這里就會(huì)被捕獲。如果不匹配,它會(huì)被傳遞給第二個(gè) catch 塊。如此,直到異常被捕獲或者通過(guò)所有的 catch 塊。
無(wú)論是否發(fā)生異常,finally 代碼塊中的代碼總會(huì)被執(zhí)行。在 finally 代碼塊中,可以做一些資源回收工作,如關(guān)閉JDBC連接。

try{
    // code 
}catch( 異常類型1 ex ){
    //..
}catch( 異常類型2 ex){
    //..
}catch( 異常類型3 ex ){
    //..
}finally{
   //..
}
throw、throws

throw 的作用是拋出一個(gè)異常,無(wú)論它是新實(shí)例化的還是剛捕獲到的。
throws 是方法可能拋出異常的聲明。使用 throws 關(guān)鍵字聲明的方法表示此方法不處理異常,而交給方法調(diào)用處進(jìn)行處理,一個(gè)方法可以聲明拋出多個(gè)異常。
例如,下面的方法聲明拋出 RemoteExceptionInsufficientFundsException

public class className
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
       // Method implementation
       if(..)
           throw new RemoteException();
       else
           throw new InsufficientFundsException();
   }
   //Remainder of class definition
}
try-with-resources 和 multiple catch

從Java 7開始提供了兩個(gè)有用的特性:try-with-resourcesmultiple catch。
try-with-resources 將 try-catch-finally 簡(jiǎn)化為 try-catch,這其實(shí)是一種語(yǔ)法糖,在編譯時(shí)會(huì)轉(zhuǎn)化為 try-catch-finally 語(yǔ)句。自動(dòng)按照約定俗成 close 那些擴(kuò)展了 AutoCloseable 或者 Closeable 的對(duì)象,從而替代了finally中關(guān)閉資源的功能。以下代碼用try-with-resources 自動(dòng)關(guān)閉 java.sql.Statement

public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) { // Try-with-resources
        ResultSet rs = stmt.executeQuery(query);

        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");

            System.out.println(coffeeName + ", " + supplierID + ", " + 
                               price + ", " + sales + ", " + total);
        }
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    }
}

值得注意的是,異常拋出機(jī)制發(fā)生了變化。在過(guò)去的 try-catch-finally 結(jié)構(gòu)中,如果 try 塊沒有發(fā)生異常時(shí),直接執(zhí)行finally塊。如果try 塊發(fā)生異常,catch 塊捕捉,然后執(zhí)行 finally 塊。

但是在 try-with-resources 結(jié)構(gòu)中,不論 try 中是否有異常,都會(huì)首先自動(dòng)執(zhí)行 close 方法,然后才判斷是否進(jìn)入 catch塊。分兩種情況討論:

try 沒有發(fā)生異常,自動(dòng)調(diào)用close方法,如果發(fā)生異常,catch 塊捕捉并處理異常。

try 發(fā)生異常,然后自動(dòng)調(diào)用 close 方法,如果 close 也發(fā)生異常,catch 塊只會(huì)捕捉 try 塊拋出的異常,close 方法的異常會(huì)在 catch 中被壓制,但是你可以在 catch 塊中,用Throwable.getSuppressed 方法來(lái)獲取到壓制異常的數(shù)組。

再來(lái)看看multiple catch ,當(dāng)我們需要同時(shí)捕獲多個(gè)異常,但是對(duì)這些異常處理的代碼是相同的。比如:

try {
    execute(); //exception might be thrown
} catch (IOException ex) {
    LOGGER.error(ex);
    throw new SpecialException();
} catch (SQLException ex) {
    LOGGER.error(ex);
    throw new SpecialException();
} 

使用 multiple catch 可以把代碼寫成下面這樣:

try {
    execute(); //exception might be thrown
} catch (IOException | SQLExceptionex ex) {// Multiple catch
    LOGGER.log(ex);
    throw new SpecialException();
}

這里需要注意的是,上面代碼中 ex是隱式的 final 不可以在catch 塊中改變ex。

自定義異常

有的時(shí)候,我們會(huì)根據(jù)需要自定義異常。自定義的所有異常都必須是 Throwable 的子類,如果是檢查型異常,則繼承 Exception 類。如果自定義的是運(yùn)行時(shí)異常,則繼承 RuntimeException。這個(gè)時(shí)候除了保證提供足夠的信息,還有兩點(diǎn)需要考慮:

是否需要定義成 Checked Exception,這種類型設(shè)計(jì)的初衷是為了從異常情況恢復(fù)。

在保證診斷信息足夠的同時(shí),也要考慮避免包含敏感信息,因?yàn)槟菢涌赡軐?dǎo)致潛在的安全問(wèn)題。例如java.net.ConnectException的出錯(cuò)信息是"Connection refused(Connection refused)",而不包含具體的機(jī)器名、IP、端口等,一個(gè)重要考量就是信息安全。類似的情況在日志中也有,比如,用戶數(shù)據(jù)一般是不可以輸出到日志里面的。

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

看下面代碼,有哪些不當(dāng)之處?

try {
    // …
    Thread.sleep(1000L);
} catch (Exception e) {
} 

以上代碼雖短,但已經(jīng)違反了異常處理的兩個(gè)基本原則。
第一,盡量不要捕獲頂層的Exception,而是應(yīng)該捕獲特定異常。 在這里是 Thread.sleep() 拋出的 InterruptedException。我們希望自己的代碼在出現(xiàn)異常時(shí)能夠盡量給出詳細(xì)的異常信息,而Exception恰恰隱藏了我們的目的,另外我們也要保證程序不會(huì)捕獲到我們不希望捕獲的異常,而上邊的代碼將捕獲所有的異常,包括 unchecked exception ,比如,你可能更希望
RuntimeException 被擴(kuò)散出來(lái),而不是被捕獲。進(jìn)一步講,盡量不要捕獲 Throwable 或者 Error,這樣很難保證我們能夠正確處理程序 OutOfMemoryError
第二,不要生吞(swallow)異常 ,這是異常處理中要特別注意的事情,因?yàn)楹芸赡軙?huì)導(dǎo)致非常難以診斷的詭異情況。當(dāng)try塊發(fā)生 checked exception 時(shí),我們應(yīng)當(dāng)采取一些補(bǔ)救措施。如果 checked exception 沒有任何意義,可以將其轉(zhuǎn)化為 unchecked exception 再重新拋出。千萬(wàn)不要用一個(gè)空的 catch 塊捕獲來(lái)忽略它,程序可能在后續(xù)代碼以不可控的方式結(jié)束,沒有人能夠輕易判斷究竟是哪里拋出了異常,以及是什么原因產(chǎn)生了異常。

try {
    // …
} catch (IOException e) {
    e.printStackTrace();
}

這段在實(shí)驗(yàn)中沒問(wèn)題的代碼通常在產(chǎn)品代碼中不允許這樣處理。
查看printStackTrace()文檔開頭就是“Prints this throwable and its backtrace to the standard error stream”,問(wèn)題就在這,在稍微復(fù)雜一點(diǎn)的生產(chǎn)系統(tǒng)中,標(biāo)準(zhǔn)出錯(cuò)(STERR)不是個(gè)合適的輸出選項(xiàng),因?yàn)楹茈y判斷出到底輸出到哪里去了。尤其是對(duì)于分布式系統(tǒng),如果發(fā)生異常,但是無(wú)法找到堆棧軌跡(stacktrace),這純屬是為診斷設(shè)置障礙。所以,最好使用產(chǎn)品日志,詳細(xì)地輸出到日志系統(tǒng)里。

Throw early, catch late 原則

This is probably the most famous principle about Exception handling. It basically says that you should throw an exception as soon as you can, and catch it late as much as possible. You should wait until you have all the information to handle it properly.

This principle implicitly says that you will be more likely to throw it in the low-level methods, where you will be checking if single values are null or not appropriate. And you will be making the exception climb the stack trace for quite several levels until you reach a sufficient level of abstraction to be able to handle the problem.
看下面的代碼段:

public void readPreferences(String fileName){
    //...perform operations...
    InputStream in = new FileInputStream(fileName);
    //...read the preferences file...
}

上段代碼中如果 fileName 為 null,那么程序就會(huì)拋出 NullPointerException,但是由于沒有第一時(shí)間暴露出問(wèn)題,堆棧信息可能非常令人費(fèi)解,往往需要相對(duì)復(fù)雜的定位。在發(fā)現(xiàn)問(wèn)題的時(shí)候,第一時(shí)間拋出,能夠更加清晰地反映問(wèn)題。

修改一下上面的代碼,讓問(wèn)題 “throw early”,對(duì)應(yīng)的異常信息就非常直觀了。

public void readPreferences(String filename) {
    Objects. requireNonNull(filename);   // throw NullPointerException
    //...perform other operations...
    InputStream in = new FileInputStream(filename);
    //...read the preferences file...
}

上面這段代碼使用了Objects.requireNonNull()方法,下面是它在java.util.Objects里的具體實(shí)現(xiàn):

public static  T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

至于 catch late,捕獲異常后,需要怎么處理呢?最差的處理方式,就是的“生吞異常”,本質(zhì)上其實(shí)是掩蓋問(wèn)題。如果實(shí)在不知道如何處理,可以選擇保留原有異常的 cause 信息,直接再拋出或者構(gòu)建新的異常拋出去。在更高層面,因?yàn)橛辛饲逦模I(yè)務(wù))邏輯,往往會(huì)更清楚合適的處理方式是什么。

異常處理機(jī)制的性能開銷

從性能角度審視一下Java的異常處理機(jī)制,有兩個(gè)可能會(huì)相對(duì)昂貴的地方:

try-catch 代碼段會(huì)產(chǎn)生額外的性能開銷,換個(gè)角度說(shuō),它往往會(huì)影響JVM對(duì)代碼進(jìn)行優(yōu)化,所以建議僅捕獲有必要的代碼段,盡量不要一個(gè)大的 try 包住整段的代碼;更不要利用異??刂拼a流程,這遠(yuǎn)比我們通常意義上的條件語(yǔ)句(if/else、switch)要低效。

Java 每實(shí)例化一個(gè) Exception,都會(huì)對(duì)當(dāng)時(shí)的棧進(jìn)行快照,這是一個(gè)相對(duì)比較重的操作。如果發(fā)生的非常頻繁,這個(gè)開銷可就不能被忽略了。

所以,對(duì)于部分追求極致性能的底層類庫(kù),有種方式是嘗試創(chuàng)建不進(jìn)行??煺盏腅xception。另外,當(dāng)我們的服務(wù)出現(xiàn)反應(yīng)變慢、吞吐量下降的時(shí)候,檢查發(fā)生最頻繁的 Exception 也是一種思路。

參考文章:

Java 異常處理 - runoob.com

Designing with exceptions :Guidelines and tips on when and how to use exceptions

Exception和Error有什么區(qū)別? - Java核心技術(shù)36講

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

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

相關(guān)文章

  • 淺析微信支付:支付結(jié)果通知

    摘要:本文是淺析微信支付系列文章的第六篇,主要講解支付成功后,微信回調(diào)商戶支付結(jié)果通知的處理。微信支付支付回調(diào)接口該鏈接是通過(guò)統(tǒng)一下單中提交的參數(shù)設(shè)置,如果鏈接無(wú)法訪問(wèn),商戶將無(wú)法接收到微信通知。 本文是【淺析微信支付】系列文章的第六篇,主要講解支付成功后,微信回調(diào)商戶支付結(jié)果通知的處理。 淺析微信支付系列已經(jīng)更新五篇了喲~,沒有看過(guò)的朋友們可以看一下哦。 淺析微信支付:統(tǒng)一下單接口 淺析...

    Dean 評(píng)論0 收藏0
  • 淺析php中的異常與錯(cuò)誤

    摘要:異常與錯(cuò)誤異常是指程序運(yùn)行中不符合預(yù)期情況以及與正常流程不同的狀況。在中主要的錯(cuò)誤等級(jí)如下最低級(jí)別的錯(cuò)誤,表示不推薦不建議。小結(jié)中錯(cuò)誤和異常是兩個(gè)不同的概念,這種設(shè)計(jì)根本上導(dǎo)致了的異常和錯(cuò)誤與其它語(yǔ)言相異。中,異常時(shí)錯(cuò)誤唯一的報(bào)告方式。 異常與錯(cuò)誤 異常是指程序運(yùn)行中不符合預(yù)期情況以及與正常流程不同的狀況。錯(cuò)誤則屬于自身問(wèn)題,是一種非法語(yǔ)法或者環(huán)境問(wèn)題導(dǎo)致的、讓編譯器無(wú)法通過(guò)檢查設(shè)置無(wú)...

    Leck1e 評(píng)論0 收藏0
  • GC(@廣告位出售)垃圾回收機(jī)制淺析與理解

    摘要:廣告位出售垃圾回收機(jī)制淺析與理解對(duì)垃圾回收進(jìn)行分析前,我們先來(lái)了解一些基本概念基本概念內(nèi)存管理內(nèi)存管理對(duì)于編程語(yǔ)言至關(guān)重要。里面的變量通常是局部變量函數(shù)參數(shù)等。 GC(@廣告位出售)垃圾回收機(jī)制: 淺析與理解 對(duì)垃圾回收進(jìn)行分析前,我們先來(lái)了解一些基本概念 基本概念 內(nèi)存管理:內(nèi)存管理對(duì)于編程語(yǔ)言至關(guān)重要。匯編允許你操作所有東西,或者說(shuō)要求你必須全權(quán)處理所有細(xì)節(jié)更合適。C 語(yǔ)言中雖然...

    songjz 評(píng)論0 收藏0
  • GC(@廣告位出售)垃圾回收機(jī)制淺析與理解

    摘要:廣告位出售垃圾回收機(jī)制淺析與理解對(duì)垃圾回收進(jìn)行分析前,我們先來(lái)了解一些基本概念基本概念內(nèi)存管理內(nèi)存管理對(duì)于編程語(yǔ)言至關(guān)重要。里面的變量通常是局部變量函數(shù)參數(shù)等。 GC(@廣告位出售)垃圾回收機(jī)制: 淺析與理解 對(duì)垃圾回收進(jìn)行分析前,我們先來(lái)了解一些基本概念 基本概念 內(nèi)存管理:內(nèi)存管理對(duì)于編程語(yǔ)言至關(guān)重要。匯編允許你操作所有東西,或者說(shuō)要求你必須全權(quán)處理所有細(xì)節(jié)更合適。C 語(yǔ)言中雖然...

    xioqua 評(píng)論0 收藏0
  • 淺析 Python 的類、繼承和多態(tài)

    摘要:類的定義假如要定義一個(gè)類,表示二維的坐標(biāo)點(diǎn)最最基本的就是方法,相當(dāng)于的構(gòu)造函數(shù)。嚴(yán)格來(lái)講,并不支持多態(tài)。靜態(tài)類型的缺失,讓很難實(shí)現(xiàn)那樣嚴(yán)格的多態(tài)檢查機(jī)制。有時(shí)候,需要在子類中調(diào)用父類的方法。 類的定義 假如要定義一個(gè)類 Point,表示二維的坐標(biāo)點(diǎn): # point.py class Point: def __init__(self, x=0, y=0): se...

    shadajin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

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