1. 程式人生 > >Java - 異常解析基礎

Java - 異常解析基礎

這樣的 習慣 希望 tof 指針 array 經歷 構造器 body

java提高篇(十六)-----異常(一)

一、為什麽要使用異常

首先我們可以明確一點就是異常的處理機制可以確保我們程序的健壯性,提高系統可用率。雖然我們不是特別喜歡看到它,但是我們不能不承認它的地位,作用。有異常就說明程序存在問題,有助於我們及時改正。在我們的程序設計當做,任何時候任何地方因為任何原因都有可能會出現異常,在沒有異常機制的時候我們是這樣處理的:通過函數的返回值來判斷是否發生了異常(這個返回值通常是已經約定好了的),調用該函數的程序負責檢查並且分析返回值。雖然可以解決異常問題,但是這樣做存在幾個缺陷:

1、 容易混淆。如果約定返回值為-11111時表示出現異常,那麽當程序最後的計算結果真的為-1111呢?

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

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

在OO中提供的異常處理機制是提供代碼健壯的強有力的方式。使用異常機制它能夠降低錯誤處理代碼的復雜度,如果不使用異常,那麽就必須檢查特定的錯誤,並在程序中的許多地方去處理它,而如果使用異常,那就不必在方法調用處進行檢查,因為異常機制將保證能夠捕獲這個錯誤,並且,只需在一個地方處理錯誤,即所謂的異常處理程序中。這種方式不僅節約代碼,而且把“概述在正常執行過程中做什麽事”的代碼和“出了問題怎麽辦”的代碼相分離。總之,與以前的錯誤處理方法相比,異常機制使代碼的閱讀、編寫和調試工作更加井井有條。(摘自《Think in java 》)。

在初學時,總是聽老師說把有可能出錯的地方記得加異常處理,剛剛開始還不明白,有時候還覺得只是多此一舉,現在隨著自己的不斷深入,代碼編寫多了,漸漸明白了異常是非常重要的。

二、基本定義

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

那麽什麽時候才會出現異常呢?只有在你當前的環境下程序無法正常運行下去,也就是說程序已經無法來正確解決問題了,這時它所就會從當前環境中跳出,並拋出異常。拋出異常後,它首先會做幾件事。首先,它會使用new創建一個異常對象,然後在產生異常的位置終止程序,並且從當前環境中彈出對異常對象的引用,這時。異常處理機制就會接管程序,並開始尋找一個恰當的地方來繼續執行程序,這個恰當的地方就是異常處理程序,它的任務就是將程序從錯誤狀態恢復,以使程序要麽換一種方法執行,要麽繼續執行下去。

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

三、異常體系

java為我們提供了非常完美的異常處理機制,使得我們可以更加專心於我們的程序,在使用異常之前我們需要了解它的體系結構:如下(該圖摘自:http://blog.csdn.net/zhangerqing/article/details/8248186)。

技術分享

從上面這幅圖可以看出,Throwable是java語言中所有錯誤和異常的超類(萬物即可拋)。它有兩個子類:Error、Exception。

其中Error為錯誤,是程序無法處理的,如OutOfMemoryError、ThreadDeath等,出現這種情況你唯一能做的就是聽之任之,交由JVM來處理,不過JVM在大多數情況下會選擇終止線程。

而Exception是程序可以處理的異常。它又分為兩種CheckedException(受撿異常),一種是UncheckedException(不受檢異常)。其中CheckException發生在編譯階段,必須要使用try…catch(或者throws)否則編譯不通過。而UncheckedException發生在運行期,具有不確定性,主要是由於程序的邏輯問題所引起的,難以排查,我們一般都需要縱觀全局才能夠發現這類的異常錯誤,所以在程序設計中我們需要認真考慮,好好寫代碼,盡量處理異常,即使產生了異常,也能盡量保證程序朝著有利方向發展。

所以:對於可恢復的條件使用被檢查的異常(CheckedException),對於程序錯誤(言外之意不可恢復,大錯已經釀成)使用運行時異常(RuntimeException)。

java的異常類實在是太多了,產生的原因也千變萬化,所以下篇博文我將會整理,統計java中經常出現的異常,望各位關註!!

錯誤和異常的區別(Error vs Exception)

1) java.lang.Error: Throwable的子類,用於標記嚴重錯誤。合理的應用程序不應該去try/catch這種錯誤。絕大多數的錯誤都是非正常的,就根本不該出現的。
java.lang.Exception: Throwable的子類,用於指示一種合理的程序想去catch的條件。即它僅僅是一種程序運行條件,而非嚴重錯誤,並且鼓勵用戶程序去catch它。

2) Error和RuntimeException 及其子類都是未檢查的異常(unchecked exceptions),而所有其他的Exception類都是檢查了的異常(checked exceptions).
checked exceptions: 通常是從一個可以恢復的程序中拋出來的,並且最好能夠從這種異常中使用程序恢復。比如FileNotFoundException, ParseException等。檢查了的異常發生在編譯階段,必須要使用try…catch(或者throws)否則編譯不通過。
unchecked exceptions: 通常是如果一切正常的話本不該發生的異常,但是的確發生了。發生在運行期,具有不確定性,主要是由於程序的邏輯問題所引起的。比如ArrayIndexOutOfBoundException, ClassCastException等。從語言本身的角度講,程序不該去catch這類異常,雖然能夠從諸如RuntimeException這樣的異常中catch並恢復,但是並不鼓勵終端程序員這麽做,因為完全沒要必要。因為這類錯誤本身就是bug,應該被修復,出現此類錯誤時程序就應該立即停止執行。 因此,面對Errors和unchecked exceptions應該讓程序自動終止執行,程序員不該做諸如try/catch這樣的事情,而是應該查明原因,修改代碼邏輯。

RuntimeException:RuntimeException體系包括錯誤的類型轉換、數組越界訪問和試圖訪問空指針等等。

處理RuntimeException的原則是:如果出現 RuntimeException,那麽一定是程序員的錯誤。例如,可以通過檢查數組下標和數組邊界來避免數組越界訪問異常。其他(IOException等等)checked異常一般是外部錯誤,例如試圖從文件尾後讀取數據等,這並不是程序本身的錯誤,而是在應用環境中出現的外部錯誤。

四、異常使用

在網上看了這樣一個搞笑的話:世界上最真情的相依,是你在try我在catch。無論你發神馬脾氣,我都默默承受,靜靜處理。對於初學者來說異常就是try…catch,(鄙人剛剛接觸時也是這麽認為的,碰到異常就是try…catch)。個人感覺try…catch確實是用的最多也是最實用的。

在異常中try快包含著可能出現異常的代碼塊,catch塊捕獲異常後對異常進行處理。先看如下實例:

public class ExceptionTest {
    public static void main(String[] args) {
        String file = "D:\\exceptionTest.txt";
        FileReader reader;
        try {
            reader = new FileReader(file);
            Scanner in = new Scanner(reader);  
            String string = in.next();  
            System.out.println(string + "不知道我有幸能夠執行到不.....");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("對不起,你執行不到...");
        }  
        finally{
            System.out.println("finally 在執行...");
        }
    }
}

從這個結果我們可以看出這些:

1、當程序遇到異常時會終止程序的運行(即後面的代碼不在執行),控制權交由異常處理機制處理。

2、catch捕捉異常後,執行裏面的函數。

不論程序是否發生異常,finally代碼塊總是會執行。所以finally一般用來關閉資源。

在這裏我們發現兩個異常之間存在如下區別:第二個異常信息多了Exception in thread "main",這顯示了出現異常信息的位置。在這裏可以得到如下結論:若程序中顯示的聲明了某個異常,則拋出異常時不會顯示出處,若程序中沒有顯示的聲明某個異常,當拋出異常時,系統會顯示異常的出處。

自定義異常

Java確實給我們提供了非常多的異常,但是異常體系是不可能預見所有的希望加以報告的錯誤,所以Java允許我們自定義異常來表現程序中可能會遇到的特定問題,總之就是一句話:我們不必拘泥於Java中已有的異常類型。

Java自定義異常的使用要經歷如下四個步驟:

1、定義一個類繼承Throwable或其子類。

2、添加構造方法(當然也可以不用添加,使用默認構造方法)。

3、在某個方法類拋出該異常。

4、捕捉該異常。

/** 自定義異常 繼承Exception類 **/
public class MyException extends Exception{
    public MyException(){
        
    }
    
    public MyException(String message){
        super(message);
    }
}

public class Test {
    public void display(int i) throws MyException{
        if(i == 0){
            throw new MyException("該值不能為0.......");
        }
        else{
            System.out.println( i / 2);
        }
    }
    
    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.display(0);
            System.out.println("---------------------");
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

六、異常鏈

在設計模式中有一個叫做責任鏈模式,該模式是將多個對象鏈接成一條鏈,客戶端的請求沿著這條鏈傳遞直到被接收、處理。同樣Java異常機制也提供了這樣一條鏈:異常鏈。

我們知道每遇到一個異常信息,我們都需要進行try…catch,一個還好,如果出現多個異常呢?分類處理肯定會比較麻煩,那就一個Exception解決所有的異常吧。這樣確實是可以,但是這樣處理勢必會導致後面的維護難度增加。最好的辦法就是將這些異常信息封裝,然後捕獲我們的封裝類即可。

誠然在應用程序中,我們有時候不僅僅只需要封裝異常,更需要傳遞。怎麽傳遞?throws!!binge,正確!!但是如果僅僅只用throws拋出異常,那麽你的封裝類,怎麽辦??

我們有兩種方式處理異常,一是throws拋出交給上級處理,二是try…catch做具體處理。但是這個與上面有什麽關聯呢?try…catch的catch塊我們可以不需要做任何處理,僅僅只用throw這個關鍵字將我們封裝異常信息主動拋出來。然後在通過關鍵字throws繼續拋出該方法異常。它的上層也可以做這樣的處理,以此類推就會產生一條由異常構成的異常鏈。

通過使用異常鏈,我們可以提高代碼的可理解性、系統的可維護性和友好性。

同理,我們有時候在捕獲一個異常後拋出另一個異常信息,並且希望將原始的異常信息也保持起來,這個時候也需要使用異常鏈。

在異常鏈的使用中,throw拋出的是一個新的異常信息,這樣勢必會導致原有的異常信息丟失,如何保持?在Throwable及其子類中的構造器中都可以接受一個cause參數,該參數保存了原有的異常信息,通過getCause()就可以獲取該原始異常信息。

語法:

public void test() throws XxxException{
        try {
            //do something:可能拋出異常信息的代碼塊
        } catch (Exception e) {
            throw new XxxException(e);
        }
    }

示例:

public class Test {
    public void f() throws MyException{
         try {
            FileReader reader = new FileReader("G:\\myfile\\struts.txt");  
             Scanner in = new Scanner(reader);  
             System.out.println(in.next());
        } catch (FileNotFoundException e) {
            //e 保存異常信息
            throw new MyException("文件沒有找到--01",e);
        }  
    }
    
    public void g() throws MyException{
        try {
            f();
        } catch (MyException e) {
            //e 保存異常信息
            throw new MyException("文件沒有找到--02",e);
        }
    }
    
    public static void main(String[] args) {
        Test t = new Test();
        try {
            t.g();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

七、異常的使用誤區

首先我們先看如下示例:該實例能夠反映java異常的不正確使用(其實這也是我剛剛學Java時寫的代碼)!!

OutputStreamWriter out = null;
        java.sql.Connection conn = null;
        try {            //   ---------1
            Statement stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select *from user");
            while (rs.next()){
                out.println("name:" + rs.getString("name") + "sex:"
                        + rs.getString("sex"));
            }
            conn.close();         //------2
            out.close();
        } 
        catch (Exception ex){    //------3
            ex.printStackTrace();    //------4
        }

1、-----------1

對於這個try…catch塊,我想他的真正目的是捕獲SQL的異常,但是這個try塊是不是包含了太多的信息了。這是我們為了偷懶而養成的代碼壞習慣。有些人喜歡將一大塊的代碼全部包含在一個try塊裏面,因為這樣省事,反正有異常它就會拋出,而不願意花時間來分析這個大代碼塊有那幾塊會產生異常,產生什麽類型的異常,反正就是一簍子全部搞定。這就想我們出去旅遊將所有的東西全部裝進一個箱子裏面,而不是分類來裝,雖不知裝進去容易,找出來難啊!!!所有對於一個異常塊,我們應該仔細分清楚每塊的拋出異常,因為一個大代碼塊有太多的地方會出現異常了。

結論一:盡可能的減小try塊!!!

2、--------2

在這裏你發現了什麽?異常改變了運行流程!!不錯就是異常改變了程序運行流程。如果該程序發生了異常那麽conn.close(); out.close();是不可能執行得到的,這樣勢必會導致資源不能釋放掉。所以如果程序用到了文件、Socket、JDBC連接之類的資源,即使遇到了異常,我們也要確保能夠正確釋放占用的資源。這裏finally就有用武之地了:不管是否出現了異常,finally總是有機會運行的,所以finally用於釋放資源是再適合不過了。

結論二:保證所有資源都被正確釋放。充分運用finally關鍵詞。

3、----------3

對於這個代碼我想大部分人都是這樣處理的,(LZ也是技術分享)。使用這樣代碼的人都有這樣一個心理,一個catch解決所有異常,這樣是可以,但是不推薦!為什麽!首先我們需要明白catch塊所表示是它預期會出現何種異常,並且需要做何種處理,而使用Exception就表示他要處理所有的異常信息,但是這樣做有什麽意義呢?

這裏我們再來看看上面的程序實例,很顯然它可能需要拋出兩個異常信息,SQLException和IOException。所以一個catch處理兩個截然不同的Exception明顯的不合適。如果用兩個catch,一個處理SQLException、一個處理IOException就好多了。所以:

結論三:catch語句應當盡量指定具體的異常類型,而不應該指定涵蓋範圍太廣的Exception類。 不要一個Exception試圖處理所有可能出現的異常。

4、----------4

這個就問題多多了,我敢保證幾乎所有的人都這麽使用過。這裏涉及到了兩個問題,一是,捕獲了異常不做處理,二是異常信息不夠明確。

4.1、捕獲異常不做處理,就是我們所謂的丟棄異常。我們都知道異常意味著程序出現了不可預期的問題,程序它希望我們能夠做出處理來拯救它,但是你呢?一句ex.printStackTrace()搞定,這是多麽的不負責任對程序的異常情況不理不顧。雖然這樣在調試可能會有一定的幫助,但是調試階段結束後呢?不是一句ex.printStackTrace()就可以搞定所有的事情的!

那麽怎麽改進呢?有四種選擇:

1、處理異常。對所發生的的異常進行一番處理,如修正錯誤、提醒。再次申明ex.printStackTrace()算不上已經“處理好了異常”.

2、重新拋出異常。既然你認為你沒有能力處理該異常,那麽你就盡情向上拋吧!!!

3、封裝異常。這是LZ認為最好的處理方法,對異常信息進行分類,然後進行封裝處理。

4、不要捕獲異常。

4.2、異常信息不明確。我想對於這樣的:java.io.FileNotFoundException: ………信息除了我們IT人沒有幾個人看得懂和想看吧!所以在出現異常後,我們最好能夠提供一些文字信息,例如當前正在執行的類、方法和其他狀態信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。起碼我公司是需要將異常信息所在的類、方法、何種異常都需要記錄在日誌文件中的。

所以:

結論四:既然捕獲了異常,就要對它進行適當的處理。不要捕獲異常之後又把它丟棄,不予理睬。 不要做一個不負責的人。

結論五:在異常處理模塊中提供適量的錯誤原因信息,組織錯誤信息使其易於理解和閱讀。

對於異常還有以下幾個註意地方:

六、不要在finally塊中處理返回值。

七、不要在構造函數中拋出異常。

八、try…catch、throw、throws

在這裏主要是區分throw和throws。

throws是方法拋出異常。在方法聲明中,如果添加了throws子句,表示該方法即將拋出異常,異常的處理交由它的調用者,至於調用者任何處理則不是它的責任範圍內的了。所以如果一個方法會有異常發生時,但是又不想處理或者沒有能力處理,就使用throws吧!

而throw是語句拋出異常。它不可以單獨使用,要麽與try…catch配套使用,要麽與throws配套使用。

//使用throws拋出異常
    public void f() throws MyException{
         try {
            FileReader reader = new FileReader("G:\\myfile\\struts.txt");  
             Scanner in = new Scanner(reader);  
             System.out.println(in.next());
        } catch (FileNotFoundException e) {
            throw new MyException("文件沒有找到", e);    //throw
        }  
        
    }

九、總結

其實對於異常使用的優缺點現在確實存在很多的討論。例如:http://www.cnblogs.com/mailingfeng/archive/2012/11/14/2769974.html。這篇博文對於是否需要使用異常進行了比較深刻的討論。LZ實乃菜鳥一枚,不能理解異常深奧之處。但是有一點LZ可以肯定,那就是異常必定會影響系統的性能。

異常使用指南(摘自:Think in java)

應該在下列情況下使用異常。

1、在恰當的級別處理問題(在知道該如何處理異常的情況下才捕獲異常)。

2、解決問題並且重新調用產生異常的方法。

3、進行少許修補,然後繞過異常發生的地方繼續執行。

4、用別的數據進行計算,以代替方法預計會返回的值。

5、把當前運行環境下能做的事情盡量做完。然後把相同(不同)的異常重新拋到更高層。

6、終止程序。

7、進行簡化。

8、讓類庫和程序更加安全。(這既是在為調試做短期投資,也是在為程序的健壯做長期投資)

Java finally語句到底是在return之前還是之後執行?

網上有很多人探討Java中異常捕獲機制try...catch...finally塊中的finally語句是不是一定會被執行?很多人都說不是,當然他們的回答是正確的,經過我試驗,至少有兩種情況下finally語句是不會被執行的:

(1)try語句沒有被執行到,如在try語句之前就返回了,這樣finally語句就不會執行,這也說明了finally語句被執行的必要而非充分條件是:相應的try語句一定被執行到。

(2)在try塊中有System.exit(0);這樣的語句,System.exit(0);是終止Java虛擬機JVM的,連JVM都停止了,所有都結束了,當然finally語句也不會被執行到。

當然還有很多人探討Finally語句的執行與return的關系,頗為讓人迷惑,不知道finally語句是在try的return之前執行還是之後執行?我也是一頭霧水,我覺得他們的說法都不正確,我覺得應該是:finally語句是在try的return語句執行之後,return返回之前執行。這樣的說法有點矛盾,也許是我表述不太清楚,下面我給出自己試驗的一些結果和示例進行佐證,有什麽問題歡迎大家提出來。

public class FinallyTest1 {

    public static void main(String[] args) {
        
        System.out.println(test1());
    }

    public static int test1() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80; 
        }
        catch (Exception e) {

            System.out.println("catch block");
        }
        finally {
            
            System.out.println("finally block");
            
            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
        }
        
        return b;
    }
    
}

運行結果是:

try block
finally block
b>25, b = 100
100

說明return語句已經執行了再去執行finally語句,不過並沒有直接返回,而是等finally語句執行完了再返回結果。

如果覺得這個例子還不足以說明這個情況的話,下面再加個例子加強證明結論:

public class FinallyTest1 {

    public static void main(String[] args) {
        
        System.out.println(test11());
    }
    
    public static String test11() {
        try {
            System.out.println("try block");

           return test12();
      } finally {
           System.out.println("finally block");
       }
  }

  public static String test12() {
       System.out.println("return statement");

       return "after return";
   }
    
}

運行結果為:

try block
return statement
finally block
after return

說明try中的return語句先執行了但並沒有立即返回,等到finally執行結束後再

這裏大家可能會想:如果finally裏也有return語句,那麽是不是就直接返回了,try中的return就不能返回了?看下面。

public class FinallyTest2 {

    public static void main(String[] args) {

        System.out.println(test2());
    }

    public static int test2() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            return 200;
        }

        // return b;
    }

}

運行結果是:

try block
finally block
b>25, b = 100
200

這說明finally裏的return直接返回了,就不管try中是否還有返回語句,這裏還有個小細節需要註意,finally裏加上return過後,finally外面的return b就變成不可到達語句了,也就是永遠不能被執行到,所以需要註釋掉否則編譯器報錯。

這裏大家可能又想:如果finally裏沒有return語句,但修改了b的值,那麽try中return返回的是修改後的值還是原值?看下面。

public class FinallyTest3 {

    public static void main(String[] args) {

        System.out.println(test3());
    }

    public static int test3() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b = 150;
        }

        return 2000;
    }

}

運行結果是:

try block
finally block
b>25, b = 100
100

測試用例2:

import java.util.*;

public class FinallyTest6
{
    public static void main(String[] args) {
        System.out.println(getMap().get("KEY").toString());
    }
     
    public static Map<String, String> getMap() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("KEY", "INIT");
         
        try {
            map.put("KEY", "TRY");
            return map;
        }
        catch (Exception e) {
            map.put("KEY", "CATCH");
        }
        finally {
            map.put("KEY", "FINALLY");
            map = null;
        }
         
        return map;
    }
}

運行結果是:

FINALLY

為什麽測試用例1中finally裏的b = 150;並沒有起到作用而測試用例2中finally的map.put("KEY", "FINALLY");起了作用而map = null;卻沒起作用呢?這就是Java到底是傳值還是傳址的問題了,具體請看精選30道Java筆試題解答,裏面有詳細的解答,簡單來說就是:Java中只有傳值沒有傳址,這也是為什麽map = null這句不起作用。這同時也說明了返回語句是try中的return語句而不是 finally外面的return b;這句,不相信的話可以試下,將return b;改為return 294,對原來的結果沒有一點影響。

這裏大家可能又要想:是不是每次返回的一定是try中的return語句呢?那麽finally外的return b不是一點作用沒嗎?請看下面。

public class FinallyTest4 {

    public static void main(String[] args) {

        System.out.println(test4());
    }

    public static int test4() {
        int b = 20;

        try {
            System.out.println("try block");

            b = b / 0;

            return b += 80;
        } catch (Exception e) {

            b += 15;
            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b += 50;
        }

        return 204;
    }

}

運行結果是:

try block
catch block
finally block
b>25, b = 35
85

這裏因 為在return之前發生了除0異常,所以try中的return不會被執行到,而是接著執行捕獲異常的catch 語句和最終的finally語句,此時兩者對b的修改都影響了最終的返回值,這時return b;就起到作用了。當然如果你這裏將return b改為return 300什麽的,最後返回的就是300,這毋庸置疑。

這裏大家可能又有疑問:如果catch中有return語句呢?當然只有在異常的情況下才有可能會執行,那麽是在finally之前就返回嗎?看下面。

public class FinallyTest5 {

    public static void main(String[] args) {

        System.out.println(test5());
    }

    public static int test5() {
        int b = 20;

        try {
            System.out.println("try block");
            
            b = b /0;

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
            return b += 15;
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b += 50;
        }

        //return b;
    }

}

運行結果如下:

try block
catch block
finally block
b>25, b = 35
35

說明了發生異常後,catch中的return語句先執行,確定了返回值後再去執行finally塊,執行完了catch再返回,finally裏對b的改變對返回值無影響,原因同前面一樣,也就是說情況與try中的return語句執行完全一樣。

最後總結:finally塊的語句在try或catch中的return語句執行之後返回之前執行且finally裏的修改語句可能影響也可能不影響try或catch中 return已經確定的返回值,若finally裏也有return語句則覆蓋try或catch中的return語句直接返回。

Java - 異常解析基礎