0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

JAVA中常見的幾個異常類型及處理方案

454398 ? 來源:阿里云 ? 作者:阿里云原創(chuàng) ? 2020-10-15 16:36 ? 次閱讀

異常簡介

先上個圖,看一下常見的幾個異常類型。

所有的異常都來自于Throwable。Throwable有兩個子類,Error和Exception。

Error通常表示的是嚴(yán)重錯誤,這些錯誤是不建議被catch的。

注意這里有一個例外,比如ThreadDeath也是繼承自Error,但是它表示的是線程的死亡,雖然不是嚴(yán)重的異常,但是因為應(yīng)用程序通常不會對這種異常進(jìn)行catch,所以也歸類到Error中。

Exception表示的是應(yīng)用程序希望catch住的異常。

在Exception中有一個很特別的異常叫做RuntimeException。RuntimeException叫做運行時異常,是不需要被顯示catch住的,所以也叫做unchecked Exception。而其他非RuntimeException的Exception則需要顯示try catch,所以也叫做checked Exception。

不要忽略checked exceptions

我們知道checked exceptions是一定要被捕獲的異常,我們在捕獲異常之后通常有兩種處理方式。

第一種就是按照業(yè)務(wù)邏輯處理異常,第二種就是本身并不處理異常,但是將異常再次拋出,由上層代碼來處理。

如果捕獲了,但是不處理,那么就是忽略checked exceptions。

接下來我們來考慮一下java中線程的中斷異常。

java中有三個非常相似的方法interrupt,interrupted和isInterrupted。

isInterrupted()只會判斷是否被中斷,而不會清除中斷狀態(tài)。

interrupted()是一個類方法,調(diào)用isInterrupted(true)判斷的是當(dāng)前線程是否被中斷。并且會清除中斷狀態(tài)。

前面兩個是判斷是否中斷的方法,而interrupt()就是真正觸發(fā)中斷的方法。

它的工作要點有下面4點:

如果當(dāng)前線程實例在調(diào)用Object類的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在該實例中調(diào)用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞狀態(tài)中時,則其中斷狀態(tài)將被清除,并將收到InterruptedException。

如果此線程在InterruptibleChannel上的I / O操作中處于被阻塞狀態(tài),則該channel將被關(guān)閉,該線程的中斷狀態(tài)將被設(shè)置為true,并且該線程將收到j(luò)ava.nio.channels.ClosedByInterruptException異常。

如果此線程在java.nio.channels.Selector中處于被被阻塞狀態(tài),則將設(shè)置該線程的中斷狀態(tài)為true,并且它將立即從select操作中返回。

如果上面的情況都不成立,則設(shè)置中斷狀態(tài)為true。

看下面的例子:

public void wrongInterrupted(){

try{

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

上面代碼中我們捕獲了一個InterruptedException,但是我們僅僅是打印出了異常信息,并沒有做任何操作。這樣程序的表現(xiàn)和沒有發(fā)送一異常一樣,很明顯是有問題的。

根據(jù)上面的介紹,我們知道,interrupted()方法會清除中斷狀態(tài),所以,如果我們自身處理不了異常的情況下,需要重新調(diào)用Thread.currentThread().interrupt()重新拋出中斷,由上層代碼負(fù)責(zé)處理,如下所示。

public void correctInterrupted(){

try{

Thread.sleep(1000);

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

不要在異常中暴露敏感信息

遇到異常的時候,通常我們需要進(jìn)行一定程度的日志輸出,從而來定位異常。但是我們在做日志輸出的時候,一定要注意不要暴露敏感信息。

下表可以看到異常信息可能會暴露的敏感信息:

除了敏感信息之外,我們還要做好日志信息的安全保護(hù)。

在處理捕獲的異常時,需要恢復(fù)對象的初始狀態(tài)

如果我們在處理異常的時候,修改了對象中某些字段的狀態(tài),在捕獲異常的時候需要怎么處理呢?

private int age=30;

public void wrongRestore(){

try{

age=20;

throw new IllegalStateException(“custom exception!”);

}catch (IllegalStateException e){

System.out.println(“we do nothing”);

}

}

上面的例子中,我們將age重置為20,然后拋出了異常。雖然拋出了異常,但是我們并沒有重置age,最后導(dǎo)致age最終被修改了。

整個restore的邏輯沒有處理完畢,但是我們部分修改了對象的數(shù)據(jù),這是很危險的。

實際上,我們需要一個重置:

public void rightRestore(){

try{

age=20;

throw new IllegalStateException(“custom exception!”);

}catch (IllegalStateException e){

System.out.println(“we do nothing”);

age=30;

}

}

不要手動完成finally block

我們在使用try-finally和try-catch-finally語句時,一定不要在finally block中使用return, break, continue或者throw語句。

為什么呢?

根據(jù)Java Language Specification(JLS)的說明,finally block一定會被執(zhí)行,不管try語句中是否拋出異常。

在try-finally和try-catch-finally語句中,如果try語句中拋出了異常R,然后finally block被執(zhí)行,這時候有兩種情況:

如果finally block正常執(zhí)行,那么try語句被終止的原因是異常R。

如果在finally block中拋出了異常S,那么try語句被終止的原因?qū)兂蒘。

我們舉個例子:

public class FinallyUsage {

public boolean wrongFinally(){

try{

throw new IllegalStateException(“my exception!”);

}finally {

System.out.println(“Code comes to here!”);

return true;

}

}

public boolean rightFinally(){

try{

throw new IllegalStateException(“my exception!”);

}finally {

System.out.println(“Code comes to here!”);

}

}

public static void main(String[] args) {

FinallyUsage finallyUsage=new FinallyUsage();

finallyUsage.wrongFinally();

finallyUsage.rightFinally();

}

}

上面的例子中,我們定義了兩個方法,一個方法中我們在finally中直接return,另一方法中,我們讓finally正常執(zhí)行完畢。

最終,我們可以看到wrongFinally將異常隱藏了,而rightFinally保留了try的異常。

同樣的,如果我們在finally block中拋出了異常,我們一定要記得對其進(jìn)行捕獲,否則將會隱藏try block中的異常信息。

不要捕獲NullPointerException和它的父類異常

通常來說NullPointerException表示程序代碼有邏輯錯誤,是需要程序員來進(jìn)行代碼邏輯修改,從而進(jìn)行修復(fù)的。

比如說加上一個null check。

不捕獲NullPointerException的原因有三個。

使用null check的開銷要遠(yuǎn)遠(yuǎn)小于異常捕獲的開銷。

如果在try block中有多個可能拋出NullPointerException的語句,我們很難定位到具體的錯誤語句。

最后,如果發(fā)生了NullPointerException,程序基本上不可能正常運行或者恢復(fù),所以我們需要提前進(jìn)行null check的判斷。

同樣的,程序也不要對NullPointerException的父類RuntimeException, Exception, or Throwable進(jìn)行捕捉。

不要throw RuntimeException, Exception, or Throwable

我們拋出異常主要是為了能夠找到準(zhǔn)確的處理異常的方法,如果直接拋出RuntimeException, Exception, 或者 Throwable就會導(dǎo)致程序無法準(zhǔn)確處理特定的異常。

通常來說我們需要自定義RuntimeException, Exception, 或者 Throwable的子類,通過具體的子類來區(qū)分具體的異常類型。

不要拋出未聲明的checked Exception

一般來說checked Exception是需要顯示catch住,或者在調(diào)用方法上使用throws做申明的。

但是我們可以通過某些手段來繞過這種限制,從而在使用checked Exception的時候不需要遵守上述規(guī)則。

當(dāng)然這樣做是需要避免的。我們看一個例子:

private static Throwable throwable;

private ThrowException() throws Throwable {

throw throwable;

}

public static synchronized void undeclaredThrow(Throwable throwable) {

ThrowException.throwable = throwable;

try {

ThrowException.class.newInstance();

} catch (InstantiationException e) {

} catch (IllegalAccessException e) {

} finally {

ThrowException.throwable = null;

}

}

上面的例子中,我們定義了一個ThrowException的private構(gòu)造函數(shù),這個構(gòu)造函數(shù)會throw一個throwable,這個throwable是從方法傳入的。

在undeclaredThrow方法中,我們調(diào)用了ThrowException.class.newInstance()實例化一個ThrowException實例,因為需要調(diào)用構(gòu)造函數(shù),所以會拋出傳入的throwable。

因為Exception是throwable的子類,如果我們在調(diào)用的時候傳入一個checked Exception,很明顯,我們的代碼并沒有對其進(jìn)行捕獲:

public static void main(String[] args) {

ThrowException.undeclaredThrow(

new Exception(“Any checked exception”));

}

怎么解決這個問題呢?換個思路,我們可以使用Constructor.newInstance()來替代class.newInstance()。

try {

Constructor constructor =

ThrowException.class.getConstructor(new Class《?》[0]);

constructor.newInstance();

} catch (InstantiationException e) {

} catch (InvocationTargetException e) {

System.out.println(“catch exception!”);

} catch (NoSuchMethodException e) {

} catch (IllegalAccessException e) {

} finally {

ThrowException.throwable = null;

}

上面的例子,我們使用Constructor的newInstance方法來創(chuàng)建對象的實例。和class.newInstance不同的是,這個方法會拋出InvocationTargetException異常,并且把所有的異常都封裝進(jìn)去。

所以,這次我們獲得了一個checked Exception。
編輯:hfy

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2952

    瀏覽量

    104484
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    503

    瀏覽量

    19636
  • 異常
    +關(guān)注

    關(guān)注

    0

    文章

    22

    瀏覽量

    9214
收藏 人收藏

    評論

    相關(guān)推薦

    檢測系統(tǒng)中常見的信號類型有哪些

    在檢測系統(tǒng)中,常見的信號類型多種多樣,這些信號類型根據(jù)被測物理量的不同而有所區(qū)別。以下是一些常見的信號類型: 位移信號 :位移信號是檢測系統(tǒng)
    的頭像 發(fā)表于 10-15 13:57 ?286次閱讀

    變壓器運行中常見異常現(xiàn)象應(yīng)如何處理呢?

    變壓器運行中常見異常現(xiàn)象應(yīng)如何處理
    的頭像 發(fā)表于 03-08 09:19 ?565次閱讀
    變壓器運行<b class='flag-5'>中常見</b>的<b class='flag-5'>異常</b>現(xiàn)象應(yīng)如何<b class='flag-5'>處理</b>呢?

    Service層的異常處理

    一般初學(xué)者學(xué)習(xí)編碼和[錯誤處理]時,先知道[編程語言]有一種處理錯誤的形式或約定(如Java就拋異常),然后就開始用這些工具。但卻忽視這問題本質(zhì):「
    的頭像 發(fā)表于 01-08 11:29 ?530次閱讀

    伺服電機應(yīng)用中常見干擾類型和產(chǎn)生途徑

    伺服電機應(yīng)用中常見干擾類型和產(chǎn)生途徑
    的頭像 發(fā)表于 01-07 17:56 ?1332次閱讀

    輸電線路常見異常處理原則

    輸電線路常見異常處理原則? 輸電線路是電力系統(tǒng)的重要組成部分,用于將發(fā)電廠產(chǎn)生的電能傳輸?shù)接脩?。然而,在輸電線路的正常運行過程中,常會出現(xiàn)一些異常情況,如斷線、短路、過載等。正確
    的頭像 發(fā)表于 12-18 15:47 ?1243次閱讀

    Java怎么排查oom異常

    Java中的OOM(Out of Memory)異常是指當(dāng)Java虛擬機的堆內(nèi)存不足以容納新的對象時拋出的異常。OOM異常是一種
    的頭像 發(fā)表于 12-05 13:47 ?1189次閱讀

    Java oom異常的原因分析

    Java中的OOM(Out of Memory)異常是指當(dāng)程序在運行過程中無法分配足夠的內(nèi)存空間時拋出的異常。在Java中,內(nèi)存分為堆內(nèi)存(Heap)和棧內(nèi)存(Stack)。堆內(nèi)存用于
    的頭像 發(fā)表于 12-05 13:43 ?731次閱讀

    byte屬于java基本類型

    是的,byte屬于Java的基本數(shù)據(jù)類型之一。Java中共有8種基本數(shù)據(jù)類型,分別是byte、short、int、long、float、double、char和boolean。byte
    的頭像 發(fā)表于 12-05 10:40 ?804次閱讀

    java有比long還大的類型

    Java中,除了基本數(shù)據(jù)類型的long之外,還有一些其他的類型可以表示比long更大的數(shù)值。 BigInteger 類型: BigInteger
    的頭像 發(fā)表于 11-30 11:25 ?2864次閱讀

    數(shù)據(jù)庫decimal對應(yīng)java什么類型

    數(shù)據(jù)庫的 decimal 類型Java中可以使用 BigDecimal 來表示。 BigDecimal 是Java中用于精確計算的數(shù)據(jù)類型,它可以表示任意長度和精度的有符號十進(jìn)制數(shù)。
    的頭像 發(fā)表于 11-30 11:06 ?3680次閱讀

    java內(nèi)存溢出排查方法

    Java內(nèi)存溢出(Memory overflow)是指Java虛擬機(JVM)中的堆內(nèi)存無法滿足對象分配的需求,導(dǎo)致程序拋出OutOfMemoryError異常。內(nèi)存溢出是Java開發(fā)
    的頭像 發(fā)表于 11-23 14:46 ?3036次閱讀

    java中常用的包有哪些

    Java是一種面向?qū)ο蟮母呒壘幊陶Z言,它具有平臺無關(guān)性和可擴展性。Java中有很多常用的包,這些包提供了豐富的類庫和工具,用于開發(fā)各種類型的應(yīng)用程序。下面是Java中一些常用的包
    的頭像 發(fā)表于 11-22 15:10 ?1287次閱讀

    java對clob類型數(shù)據(jù)怎么處理

    Java處理CLOB類型數(shù)據(jù)時,我們可以使用JDBC API提供的方法來讀取、寫入和處理CLOB數(shù)據(jù)。CLOB(Character Large Object)
    的頭像 發(fā)表于 11-21 10:35 ?937次閱讀

    java對clob類型數(shù)據(jù)怎么處理

    處理CLOB類型數(shù)據(jù)在Java中是一項非常常見和重要的任務(wù)。CLOB(Character Large Object)是一種用于存儲大量字符數(shù)據(jù)的數(shù)據(jù)
    的頭像 發(fā)表于 11-21 10:30 ?2293次閱讀

    java中obj類型的實戰(zhàn)用法

    類型具有許多實戰(zhàn)用法,下面將詳細(xì)介紹一些常見的使用場景。 作為方法參數(shù)和返回值的類型Java中,可以使用obj類型作為方法的參數(shù)或返回值
    的頭像 發(fā)表于 11-21 10:27 ?692次閱讀