Java異常怎么理解

這篇文章主要講解了“Java異常怎么理解”,文中的講解內(nèi)容簡單清晰,易于學(xué)習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習“Java異常怎么理解”吧!

讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名申請、網(wǎng)絡(luò)空間、營銷軟件、網(wǎng)站建設(shè)、南樂網(wǎng)站維護、網(wǎng)站推廣。

為什么要使用異常

首先我們可以明確一點就是異常的處理機制可以確保我們程序的健壯性,提高系統(tǒng)可用率。雖然我們不是特別喜歡看到它,但是我們不能不承認它的地位,作用。

在沒有異常機制的時候我們是這樣處理的:通過函數(shù)的返回值來判斷是否發(fā)生了異常(這個返回值通常是已經(jīng)約定好了的),調(diào)用該函數(shù)的程序負責檢查并且分析返回值。雖然可以解決異常問題,但是這樣做存在幾個缺陷:

1、 容易混淆。如果約定返回值為-11111時表示出現(xiàn)異常,那么當程序最后的計算結(jié)果真的為-1111呢?

2、 代碼可讀性差。將異常處理代碼和程序代碼混淆在一起將會降低代碼的可讀性。

3、 由調(diào)用函數(shù)來分析異常,這要求程序員對庫函數(shù)有很深的了解。

在OO中提供的異常處理機制是提供代碼健壯的強有力的方式。使用異常機制它能夠降低錯誤處理代碼的復(fù)雜度,如果不使用異常,那么就必須檢查特定的錯誤,并在程序中的許多地方去處理它。

而如果使用異常,那就不必在方法調(diào)用處進行檢查,因為異常機制將保證能夠捕獲這個錯誤,并且,只需在一個地方處理錯誤,即所謂的異常處理程序中。

這種方式不僅節(jié)約代碼,而且把“概述在正常執(zhí)行過程中做什么事”的代碼和“出了問題怎么辦”的代碼相分離??傊?,與以前的錯誤處理方法相比,異常機制使代碼的閱讀、編寫和調(diào)試工作更加井井有條。

異?;径x

在《Think in java》中是這樣定義異常的:異常情形是指阻止當前方法或者作用域繼續(xù)執(zhí)行的問題。在這里一定要明確一點:異常代碼某種程度的錯誤,盡管Java有異常處理機制,但是我們不能以“正常”的眼光來看待異常,異常處理機制的原因就是告訴你:這里可能會或者已經(jīng)產(chǎn)生了錯誤,您的程序出現(xiàn)了不正常的情況,可能會導(dǎo)致程序失??!

那么什么時候才會出現(xiàn)異常呢?只有在你當前的環(huán)境下程序無法正常運行下去,也就是說程序已經(jīng)無法來正確解決問題了,這時它所就會從當前環(huán)境中跳出,并拋出異常。拋出異常后,它首先會做幾件事。

首先,它會使用new創(chuàng)建一個異常對象,然后在產(chǎn)生異常的位置終止程序,并且從當前環(huán)境中彈出對異常對象的引用,這時。異常處理機制就會接管程序,并開始尋找一個恰當?shù)牡胤絹砝^續(xù)執(zhí)行程序,這個恰當?shù)牡胤骄褪钱惓L幚沓绦颉?/p>

總的來說異常處理機制就是當程序發(fā)生異常時,它強制終止程序運行,記錄異常信息并將這些信息反饋給我們,由我們來確定是否處理異常。

異常體系

Throwable是java語言中所有錯誤和異常的超類(萬物即可拋)。它有兩個子類:Error、Exception。

Java標準庫內(nèi)建了一些通用的異常,這些類以Throwable為頂層父類。

Throwable又派生出Error類和Exception類。

錯誤:Error類以及他的子類的實例,代表了JVM本身的錯誤。錯誤不能被程序員通過代碼處理,Error很少出現(xiàn)。因此,程序員應(yīng)該關(guān)注Exception為父類的分支下的各種異常類。

異常:Exception以及他的子類,代表程序運行時發(fā)送的各種不期望發(fā)生的事件??梢员籎ava異常處理機制使用,是異常處理的核心。

總體上我們根據(jù)Javac對異常的處理要求,將異常類分為2類。

非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發(fā)現(xiàn)這樣的異常,不要求在程序處理這些異常。所以如果愿意,我們可以編寫代碼處理(使用try…catch…finally)這樣的異常,也可以不處理。

對于這些異常,我們應(yīng)該修正代碼,而不是去通過異常處理器處理 。這樣的異常發(fā)生的原因多半是代碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制類型轉(zhuǎn)換錯誤ClassCastException,數(shù)組索引越界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。

檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強制要求程序員為這樣的異常做預(yù)備處理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch語句捕獲它并處理,要么用throws子句聲明拋出它,否則編譯不會通過。

這樣的異常一般是由程序的運行環(huán)境導(dǎo)致的。因為程序可能被運行在各種未知的環(huán)境下,而程序員無法干預(yù)用戶如何使用他編寫的程序,于是程序員就應(yīng)該為這樣的異常時刻準備著。如SQLException , IOException,ClassNotFoundException 等。

需要明確的是:檢查和非檢查是對于javac來說的,這樣就很好理解和區(qū)分了。

初識異常

異常是在執(zhí)行某個函數(shù)時引發(fā)的,而函數(shù)又是層級調(diào)用,形成調(diào)用棧的,因為,只要一個函數(shù)發(fā)生了異常,那么他的所有的caller都會被異常影響。當這些被影響的函數(shù)以異常信息輸出時,就形成的了異常追蹤棧。

異常最先發(fā)生的地方,叫做異常拋出點。

public class 異常 {
    public static void main (String [] args )
    {
        System . out. println( "----歡迎使用命令行除法計算器----" ) ;
        CMDCalculate ();
    }
    public static void CMDCalculate ()
    {
        Scanner scan = new Scanner ( System. in );
        int num1 = scan .nextInt () ;
        int num2 = scan .nextInt () ;
        int result = devide (num1 , num2 ) ;
        System . out. println( "result:" + result) ;
        scan .close () ;
    }
    public static int devide (int num1, int num2 ){
        return num1 / num2 ;
    }
//    ----歡迎使用命令行除法計算器----
//            1
//            0
//    Exception in thread "main" java.lang.ArithmeticException: / by zero
//    at com.javase.異常.異常.devide(異常.java:24)
//    at com.javase.異常.異常.CMDCalculate(異常.java:19)
//    at com.javase.異常.異常.main(異常.java:12)
//  ----歡迎使用命令行除法計算器----
//    r
//    Exception in thread "main" java.util.InputMismatchException
//    at java.util.Scanner.throwFor(Scanner.java:864)
//    at java.util.Scanner.next(Scanner.java:1485)
//    at java.util.Scanner.nextInt(Scanner.java:2117)
//    at java.util.Scanner.nextInt(Scanner.java:2076)
//    at com.javase.異常.異常.CMDCalculate(異常.java:17)
//    at com.javase.異常.異常.main(異常.java:12)

從上面的例子可以看出,當devide函數(shù)發(fā)生除0異常時,devide函數(shù)將拋出ArithmeticException異常,因此調(diào)用他的CMDCalculate函數(shù)也無法正常完成,因此也發(fā)送異常,而CMDCalculate的caller——main 因為CMDCalculate拋出異常,也發(fā)生了異常,這樣一直向調(diào)用棧的棧底回溯。

這種行為叫做異常的冒泡,異常的冒泡是為了在當前發(fā)生異常的函數(shù)或者這個函數(shù)的caller中找到最近的異常處理程序。由于這個例子中沒有使用任何異常處理機制,因此異常最終由main函數(shù)拋給JRE,導(dǎo)致程序終止。

上面的代碼不使用異常處理機制,也可以順利編譯,因為2個異常都是非檢查異常。但是下面的例子就必須使用異常處理機制,因為異常是檢查異常。

代碼中我選擇使用throws聲明異常,讓函數(shù)的調(diào)用者去處理可能發(fā)生的異常。但是為什么只throws了IOException呢?因為FileNotFoundException是IOException的子類,在處理范圍內(nèi)。

異常和錯誤

下面看一個例子

//錯誤即error一般指jvm無法處理的錯誤
//異常是Java定義的用于簡化錯誤處理流程和定位錯誤的一種工具。
public class 錯誤和錯誤 {
    Error error = new Error();
    public static void main(String[] args) {
        throw new Error();
    }
    //下面這四個異?;蛘咤e誤有著不同的處理方法
    public void error1 (){
        //編譯期要求必須處理,因為這個異常是最頂層異常,包括了檢查異常,必須要處理
        try {
            throw new Throwable();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    //Exception也必須處理。否則報錯,因為檢查異常都繼承自exception,所以默認需要捕捉。
    public void error2 (){
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //error可以不處理,編譯不報錯,原因是虛擬機根本無法處理,所以啥都不用做
    public void error3 (){
        throw new Error();
    }
    //runtimeexception眾所周知編譯不會報錯
    public void error4 (){
        throw new RuntimeException();
    }
//    Exception in thread "main" java.lang.Error
//    at com.javase.異常.錯誤.main(錯誤.java:11)
}

異常的處理方式

在編寫代碼處理異常時,對于檢查異常,有2種不同的處理方式:

使用try…catch…finally語句塊處理它。

或者,在函數(shù)簽名中使用throws 聲明交給函數(shù)調(diào)用者caller去解決。

下面看幾個具體的例子,包括error,exception和throwable

上面的例子是運行時異常,不需要顯示捕獲。 下面這個例子是可檢查異常需,要顯示捕獲或者拋出。

@Test
public void testException() throws IOException
{
    //FileInputStream的構(gòu)造函數(shù)會拋出FileNotFoundException
    FileInputStream fileIn = new FileInputStream("E:\\a.txt");
    int word;
    //read方法會拋出IOException
    while((word =  fileIn.read())!=-1)
    {
        System.out.print((char)word);
    }
    //close方法會拋出IOException
    fileIn.close();
}

一般情況下的處理方式 try catch finally

public class 異常處理方式 {
@Test
public void main() {
    try{
        //try塊中放可能發(fā)生異常的代碼。
        InputStream inputStream = new FileInputStream("a.txt");
        //如果執(zhí)行完try且不發(fā)生異常,則接著去執(zhí)行finally塊和finally后面的代碼(如果有的話)。
        int i = 1/0;
        //如果發(fā)生異常,則嘗試去匹配catch塊。
        throw new SQLException();
        //使用1.8jdk同時捕獲多個異常,runtimeexception也可以捕獲。只是捕獲后虛擬機也無法處理,所以不建議捕獲。
    }catch(SQLException | IOException | ArrayIndexOutOfBoundsException exception){
        System.out.println(exception.getMessage());
        //每一個catch塊用于捕獲并處理一個特定的異常,或者這異常類型的子類。Java7中可以將多個異常聲明在一個catch中。
        //catch后面的括號定義了異常類型和異常參數(shù)。如果異常與之匹配且是最先匹配到的,則虛擬機將使用這個catch塊來處理異常。
        //在catch塊中可以使用這個塊的異常參數(shù)來獲取異常的相關(guān)信息。異常參數(shù)是這個catch塊中的局部變量,其它塊不能訪問。
        //如果當前try塊中發(fā)生的異常在后續(xù)的所有catch中都沒捕獲到,則先去執(zhí)行finally,然后到這個函數(shù)的外部caller中去匹配異常處理器。
        //如果try中沒有發(fā)生異常,則所有的catch塊將被忽略。
    }catch(Exception exception){
        System.out.println(exception.getMessage());
        //...
    }finally{
        //finally塊通常是可選的。
        //無論異常是否發(fā)生,異常是否匹配被處理,finally都會執(zhí)行。
        //finally主要做一些清理工作,如流的關(guān)閉,數(shù)據(jù)庫連接的關(guān)閉等。
    }

一個try至少要跟一個catch或者finally

    try {
        int i = 1;
    }finally {
        //一個try至少要有一個catch塊,否則, 至少要有1個finally塊。但是finally不是用來處理異常的,finally不會捕獲異常。
    }
}

異常出現(xiàn)時該方法后面的代碼不會運行,即使異常已經(jīng)被捕獲。這里舉出一個奇特的例子,在catch里再次使用try catch finally

@Test
public void test() {
    try {
        throwE();
        System.out.println("我前面拋出異常了");
        System.out.println("我不會執(zhí)行了");
    } catch (StringIndexOutOfBoundsException e) {
        System.out.println(e.getCause());
    }catch (Exception ex) {
    //在catch塊中仍然可以使用try catch finally
        try {
            throw new Exception();
        }catch (Exception ee) {
            
        }finally {
            System.out.println("我所在的catch塊沒有執(zhí)行,我也不會執(zhí)行的");
        }
    }
}
//在方法聲明中拋出的異常必須由調(diào)用方法處理或者繼續(xù)往上拋,
// 當拋到j(luò)re時由于無法處理終止程序
public void throwE (){
//        Socket socket = new Socket("127.0.0.1", 80);
        //手動拋出異常時,不會報錯,但是調(diào)用該方法的方法需要處理這個異常,否則會出錯。
//        java.lang.StringIndexOutOfBoundsException
//        at com.javase.異常.異常處理方式.throwE(異常處理方式.java:75)
//        at com.javase.異常.異常處理方式.test(異常處理方式.java:62)
        throw new StringIndexOutOfBoundsException();
    }

其實有的語言在遇到異常后仍然可以繼續(xù)運行

有的編程語言當異常被處理后,控制流會恢復(fù)到異常拋出點接著執(zhí)行,這種策略叫做:resumption model of exception handling(恢復(fù)式異常處理模式 )

而Java則是讓執(zhí)行流恢復(fù)到處理了異常的catch塊后接著執(zhí)行,這種策略叫做:termination model of exception handling(終結(jié)式異常處理模式)

"不負責任"的throws

throws是另一種處理異常的方式,它不同于try…catch…finally,throws僅僅是將函數(shù)中可能出現(xiàn)的異常向調(diào)用者聲明,而自己則不具體處理。

采取這種異常處理的原因可能是:方法本身不知道如何處理這樣的異常,或者說讓調(diào)用者處理更好,調(diào)用者需要為可能發(fā)生的異常負責。

public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN
{ 
     //foo內(nèi)部可以拋出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 類的異常,或者他們的子類的異常對象。
}

糾結(jié)的finally

finally塊不管異常是否發(fā)生,只要對應(yīng)的try執(zhí)行了,則它一定也執(zhí)行。只有一種方法讓finally塊不執(zhí)行:System.exit()。因此finally塊通常用來做資源釋放操作:關(guān)閉文件,關(guān)閉數(shù)據(jù)庫連接等等。

良好的編程習慣是:在try塊中打開資源,在finally塊中清理釋放這些資源。

需要注意的地方:

1、finally塊沒有處理異常的能力。處理異常的只能是catch塊。

2、在同一try…catch…finally塊中 ,如果try中拋出異常,且有匹配的catch塊,則先執(zhí)行catch塊,再執(zhí)行finally塊。如果沒有catch塊匹配,則先執(zhí)行finally,然后去外面的調(diào)用者中尋找合適的catch塊。

3、在同一try…catch…finally塊中 ,try發(fā)生異常,且匹配的catch塊中處理異常時也拋出異常,那么后面的finally也會執(zhí)行:首先執(zhí)行finally塊,然后去外圍調(diào)用者中尋找合適的catch塊。

public class finally使用 {
    public static void main(String[] args) {
        try {
            throw new IllegalAccessException();
        }catch (IllegalAccessException e) {
            // throw new Throwable();
            //此時如果再拋異常,finally無法執(zhí)行,只能報錯。
            //finally無論何時都會執(zhí)行
            //除非我顯示調(diào)用。此時finally才不會執(zhí)行
            System.exit(0);
        }finally {
            System.out.println("算你狠");
        }
    }
}

throw : JRE也使用的關(guān)鍵字

throw exceptionObject

程序員也可以通過throw語句手動顯式的拋出一個異常。throw語句的后面必須是一個異常對象。

throw 語句必須寫在函數(shù)中,執(zhí)行throw 語句的地方就是一個異常拋出點,==它和由JRE自動形成的異常拋出點沒有任何差別。==

public void save(User user)
{
      if(user  == null) 
          throw new IllegalArgumentException("User對象為空");
      //......
 
}

后面開始的大部分內(nèi)容都摘自http://www.cnblogs.com/lulipro/p/7504267.html

該文章寫的十分細致到位,令人欽佩,是我目前為之看到關(guān)于異常最詳盡的文章,可以說是站在巨人的肩膀上了。

異常調(diào)用鏈

異常的鏈化

在一些大型的,模塊化的軟件開發(fā)中,一旦一個地方發(fā)生異常,則如骨牌效應(yīng)一樣,將導(dǎo)致一連串的異常。假設(shè)B模塊完成自己的邏輯需要調(diào)用A模塊的方法,如果A模塊發(fā)生異常,則B也將不能完成而發(fā)生異常。

==但是B在拋出異常時,會將A的異常信息掩蓋掉,這將使得異常的根源信息丟失。異常的鏈化可以將多個模塊的異常串聯(lián)起來,使得異常信息不會丟失。==

異常鏈化:以一個異常對象為參數(shù)構(gòu)造新的異常對象。新的異對象將包含先前異常的信息。這項技術(shù)主要是異常類的一個帶Throwable參數(shù)的函數(shù)來實現(xiàn)的。這個當做參數(shù)的異常,我們叫他根源異常(cause)。

查看Throwable類源碼,可以發(fā)現(xiàn)里面有一個Throwable字段cause,就是它保存了構(gòu)造時傳遞的根源異常參數(shù)。這種設(shè)計和鏈表的結(jié)點類設(shè)計如出一轍,因此形成鏈也是自然的了。

public class Throwable implements Serializable {
    private Throwable cause = this;
 
    public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }
     public Throwable(Throwable cause) {
        fillInStackTrace();
        detailMessage = (cause==null ? null : cause.toString());
        this.cause = cause;
    }
 
    //........
}

下面看一個比較實在的異常鏈例子哈

public class 異常鏈 {
    @Test
    public void test() {
        C();
    }
    public void A () throws Exception {
        try {
            int i = 1;
            i = i / 0;
            //當我注釋掉這行代碼并使用B方法拋出一個error時,運行結(jié)果如下
//            四月 27, 2018 10:12:30 下午 org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines
//            信息: Discovered TestEngines with IDs: [junit-jupiter]
//            java.lang.Error: B也犯了個錯誤
//            at com.javase.異常.異常鏈.B(異常鏈.java:33)
//            at com.javase.異常.異常鏈.C(異常鏈.java:38)
//            at com.javase.異常.異常鏈.test(異常鏈.java:13)
//            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//            Caused by: java.lang.Error
//            at com.javase.異常.異常鏈.B(異常鏈.java:29)
        }catch (ArithmeticException e) {
            //這里通過throwable類的構(gòu)造方法將最底層的異常重新包裝并拋出,此時注入了A方法的信息。最后打印棧信息時可以看到caused by
            A方法的異常。
            //如果直接拋出,棧信息打印結(jié)果只能看到上層方法的錯誤信息,不能看到其實是A發(fā)生了錯誤。
            //所以需要包裝并拋出
            throw new Exception("A方法計算錯誤", e);
        }
    }
    public void B () throws Exception,Error {
        try {
            //接收到A的異常,
            A();
            throw new Error();
        }catch (Exception e) {
            throw e;
        }catch (Error error) {
            throw new Error("B也犯了個錯誤", error);
        }
    }
    public void C () {
        try {
            B();
        }catch (Exception | Error e) {
            e.printStackTrace();
        }
    }
    //最后結(jié)果
//    java.lang.Exception: A方法計算錯誤
//    at com.javase.異常.異常鏈.A(異常鏈.java:18)
//    at com.javase.異常.異常鏈.B(異常鏈.java:24)
//    at com.javase.異常.異常鏈.C(異常鏈.java:31)
//    at com.javase.異常.異常鏈.test(異常鏈.java:11)
//    省略
//    Caused by: java.lang.ArithmeticException: / by zero
//    at com.javase.異常.異常鏈.A(異常鏈.java:16)
//            ... 31 more
}

自定義異常

如果要自定義異常類,則擴展Exception類即可,因此這樣的自定義異常都屬于檢查異常(checked exception)。如果要自定義非檢查異常,則擴展自RuntimeException。

按照國際慣例,自定義的異常應(yīng)該總是包含如下的構(gòu)造函數(shù):

一個無參構(gòu)造函數(shù) 一個帶有String參數(shù)的構(gòu)造函數(shù),并傳遞給父類的構(gòu)造函數(shù)。 一個帶有String參數(shù)和Throwable參數(shù),并都傳遞給父類構(gòu)造函數(shù) 一個帶有Throwable 參數(shù)的構(gòu)造函數(shù),并傳遞給父類的構(gòu)造函數(shù)。 下面是IOException類的完整源代碼,可以借鑒。

public class IOException extends Exception
{
    static final long serialVersionUID = 7818375828146090155L;
 
    public IOException()
    {
        super();
    }
 
    public IOException(String message)
    {
        super(message);
    }
 
    public IOException(String message, Throwable cause)
    {
        super(message, cause);
    }
 
    public IOException(Throwable cause)
    {
        super(cause);
    }
}

異常的注意事項

異常的注意事項

當子類重寫父類的帶有 throws聲明的函數(shù)時,其throws聲明的異常必須在父類異常的可控范圍內(nèi)——用于處理父類的throws方法的異常處理器,必須也適用于子類的這個帶throws方法 。這是為了支持多態(tài)。

例如,父類方法throws 的是2個異常,子類就不能throws 3個及以上的異常。父類throws IOException,子類就必須throws IOException或者IOException的子類。

至于為什么?我想,也許下面的例子可以說明。

class Father
{
    public void start() throws IOException
    {
        throw new IOException();
    }
}
 
class Son extends Father
{
    public void start() throws Exception
    {
        throw new SQLException();
    }
}

/**********************假設(shè)上面的代碼是允許的(實質(zhì)是錯誤的)***********************/

class Test
{
    public static void main(String[] args)
    {
        Father[] objs = new Father[2];
        objs[0] = new Father();
        objs[1] = new Son();
 
        for(Father obj:objs)
        {
        //因為Son類拋出的實質(zhì)是SQLException,而IOException無法處理它。
        //那么這里的try。。catch就不能處理Son中的異常。
        //多態(tài)就不能實現(xiàn)了。
            try {
                 obj.start();
            }catch(IOException)
            {
                 //處理IOException
            }
         }
   }
}

==Java的異常執(zhí)行流程是線程獨立的,線程之間沒有影響==

Java程序可以是多線程的。每一個線程都是一個獨立的執(zhí)行流,獨立的函數(shù)調(diào)用棧。如果程序只有一個線程,那么沒有被任何代碼處理的異常 會導(dǎo)致程序終止。如果是多線程的,那么沒有被任何代碼處理的異常僅僅會導(dǎo)致異常所在的線程結(jié)束。

也就是說,Java中的異常是線程獨立的,線程的問題應(yīng)該由線程自己來解決,而不要委托到外部,也不會直接影響到其它線程的執(zhí)行。

下面看一個例子

public class 多線程的異常 {
    @Test
    public void test() {
        go();
    }
    public void go () {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0;i <= 2;i ++) {
            int finalI = i;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            executorService.execute(new Runnable() {
                @Override
                //每個線程拋出異常時并不會影響其他線程的繼續(xù)執(zhí)行
                public void run() {
                    try {
                        System.out.println("start thread" + finalI);
                        throw new Exception();
                    }catch (Exception e) {
                        System.out.println("thread" + finalI + " go wrong");
                    }
                }
            });
        }
//        結(jié)果:
//        start thread0
//        thread0 go wrong
//        start thread1
//        thread1 go wrong
//        start thread2
//        thread2 go wrong
    }
}

當finally遇上return

首先一個不容易理解的事實:

在 try塊中即便有return,break,continue等改變執(zhí)行流的語句,finally也會執(zhí)行。

public static void main(String[] args)
{
    int re = bar();
    System.out.println(re);
}
private static int bar() 
{
    try{
        return 5;
    } finally{
        System.out.println("finally");
    }
}
/*輸出:
finally
*/

很多人面對這個問題時,總是在歸納執(zhí)行的順序和規(guī)律,不過我覺得還是很難理解。我自己總結(jié)了一個方法。用如下GIF圖說明。

Java異常怎么理解cdn.xitu.io/2019/4/7/169f68268c972645?w=550&h=455&f=gif&s=122884">

也就是說:try…catch…finally中的return 只要能執(zhí)行,就都執(zhí)行了,他們共同向同一個內(nèi)存地址(假設(shè)地址是0×80)寫入返回值,后執(zhí)行的將覆蓋先執(zhí)行的數(shù)據(jù),而真正被調(diào)用者取的返回值就是最后一次寫入的。那么,按照這個思想,下面的這個例子也就不難理解了。

finally中的return 會覆蓋 try 或者catch中的返回值。

public static void main(String[] args)
    {
        int result;
 
        result  =  foo();
        System.out.println(result);     /////////2
 
        result = bar();
        System.out.println(result);    /////////2
    }
 
    @SuppressWarnings("finally")
    public static int foo()
    {
        trz{
            int a = 5 / 0;
        } catch (Exception e){
            return 1;
        } finally{
            return 2;
        }
 
    }
 
    @SuppressWarnings("finally")
    public static int bar()
    {
        try {
            return 1;
        }finally {
            return 2;
        }
    }

finally中的return會抑制(消滅)前面try或者catch塊中的異常

class TestException
{
    public static void main(String[] args)
    {
        int result;
        try{
            result = foo();
            System.out.println(result);           //輸出100
        } catch (Exception e){
            System.out.println(e.getMessage());    //沒有捕獲到異常
        }
 
        try{
            result  = bar();
            System.out.println(result);           //輸出100
        } catch (Exception e){
            System.out.println(e.getMessage());    //沒有捕獲到異常
        }
    }
 
    //catch中的異常被抑制
    @SuppressWarnings("finally")
    public static int foo() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }catch(ArithmeticException amExp) {
            throw new Exception("我將被忽略,因為下面的finally中使用了return");
        }finally {
            return 100;
        }
    }
 
    //try中的異常被抑制
    @SuppressWarnings("finally")
    public static int bar() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }finally {
            return 100;
        }
    }
}

finally中的異常會覆蓋(消滅)前面try或者catch中的異常

class TestException
{
    public static void main(String[] args)
    {
        int result;
        try{
            result = foo();
        } catch (Exception e){
            System.out.println(e.getMessage());    //輸出:我是finaly中的Exception
        }
 
        try{
            result  = bar();
        } catch (Exception e){
            System.out.println(e.getMessage());    //輸出:我是finaly中的Exception
        }
    }
 
    //catch中的異常被抑制
    @SuppressWarnings("finally")
    public static int foo() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }catch(ArithmeticException amExp) {
            throw new Exception("我將被忽略,因為下面的finally中拋出了新的異常");
        }finally {
            throw new Exception("我是finaly中的Exception");
        }
    }
 
    //try中的異常被抑制
    @SuppressWarnings("finally")
    public static int bar() throws Exception
    {
        try {
            int a = 5/0;
            return 1;
        }finally {
            throw new Exception("我是finaly中的Exception");
        }
 
    }
}

上面的3個例子都異于常人的編碼思維,因此我建議:

不要在fianlly中使用return。

不要在finally中拋出異常。

減輕finally的任務(wù),不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的。

將盡量將所有的return寫在函數(shù)的最后面,而不是try … catch … finally中。

感謝各位的閱讀,以上就是“Java異常怎么理解”的內(nèi)容了,經(jīng)過本文的學(xué)習后,相信大家對Java異常怎么理解這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

當前標題:Java異常怎么理解
分享網(wǎng)址:http://bm7419.com/article44/pcccee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、網(wǎng)站內(nèi)鏈Google、營銷型網(wǎng)站建設(shè)網(wǎng)站營銷、標簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計