六個例子徹底理解finally語句塊

六個例子徹底理解finally語句塊

這篇博客主要弄清楚兩個問題

   1. finally塊中的代碼是否一定會執行

   2. finally塊中的代碼什麼時候被執行

首先開始第一個:

finally塊中的代碼一定會被執行麼?

答案是否定的,主要有以下幾種情況:

  1.try之前發生異常或者直接結束的情況.

  finally是與try , catch配套使用的,finally生效的最大的前提就是得能進行到try語句內,例如以下這種情況就屬於沒有進入到try然後finally不執行的情況:

        int i = 5 / 0;
        try {
            System.out.println("Try block");
        } catch (Exception e) {
            System.out.println("Catch block");
        }finally {
            System.out.println("Finally block");
        }

  2.try語句內強制退出程序

  比較好理解,如下:

        try {
            System.out.println("Try block");
            System.exit(0);
        } catch (Exception e) {
            System.out.println("Catch block");
        }finally {
            System.out.println("Finally block");
        }

        //輸出
        Try block

finally是什麼時候執行的?

先看實例:以下程序的輸出結果是什麼?

public static int test() {
        try {
            return 1;
        } catch (Exception e) {
            return 0;
        } finally {
            System.out.println("Finally block");
        }
    }

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

結果:

Finally block
1

爲何會出現這種情況?

Java語言的異常處理中,finally的作用就是爲了保證無論出現什麼情況,finally裏面的代碼一定會執行(當然並不能滿足所有的情況).

由於程序執行return就意味着結束對當前函數的調用並且跳出這個函數,因此所有的必須要執行的語句都應該在return之前執行(除非遇到exit函數).因此finally塊中的函數也是要在return之前執行的.

如下圖:

這裏寫圖片描述

至於進行的什麼處理下文再說

再看一個實例:

public static int test() {
        try {
            return 1;
        } catch (Exception e) {
            return 0;
        } finally {
            System.out.println("Finally block");
            return 3;
        }
    }

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

結果:

Finally block
3

這個實例說明如果finally塊內也包含return語句,那麼finally內的return語句會覆蓋try-catch內的return結果

再來一個例子:

    public static int test() {
        int result = 0;
        try {
            result = 1;
            return result;
        } catch (Exception e) {
            result = 2;
            return result;
        } finally {
            result = 3;
            System.out.println("Finally block");
        }
    }

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

結果:

Finally block
1

先不急於解釋這種現象,再看一個類似的例子

 public static StringBuilder test() {
        StringBuilder builder = new StringBuilder("hello");
        try {
            return builder;
        } catch (Exception e) {
            return null;
        } finally {
            builder.append("world");
            System.out.println("Finally block");
        }
    }

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

結果:

Finally block
hello world

對比前面兩個例子是不是更加疑惑了,但其實原因特別簡單:

  一個方法內部定義的變量都是存儲在棧中的,當這個函數結束後,其對應的棧就會被系統回收,此時其方法內部定義的變量將不存在了,因此return在返回的時候不是直接返回方法中的變量的值,而是複製一份,然後返回.

  因此,對於前一個例子當中,由於是基本類型的數據,所有複製的是變量的數值,並且這個複製過程是在執行finally之前的,所有finally內修改基本類型的值沒能生效,因爲要輸出的值早已經確定了

  而後一個例子中是引用類型,複製的時候只是複製的對象的地址,finally對變量進行修改的時候改變了對應的對象的屬性,輸出的時候按照地址獲取屬性,獲取的是修改後的屬性,所以是有影響的.

#現在知道前面的處理是啥了.如圖:

這裏寫圖片描述

是不是已將明瞭了一些了,那麼再來看一個例子:

    public static String test() {
        StringBuilder builder = new StringBuilder("hello");
        try {
            return builder.toString();
        } catch (Exception e) {
            return null;
        } finally {
            builder.append(" world");
            System.out.println("Finally block");
        }
    }

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

輸出結果

Finally block
hello

細心的同學應該不會被坑到,finally內的修改沒有生效,原因在於複製的時候不是複製的StringBuilder的地址,而是一個字符串的地址,當然也有同學會說String也是引用類型,finally內的代碼修改了對應地址的數據,但是要注意String是一個不可變類,builder調用的append方法並沒有對"hello"進行任何修改,之前String的地址對應的值是不會變化的.所以finally內的修改沒有生效.有的同學有疑惑,關於String , StringBuilder等對象的區別請參考這篇博客:Character , String , StringBuffer , StringBuilder , StringTokenizer 的區別.

爲了對比那再看一個例子:

 static class TestEntity {
        String mString;
        StringBuilder mBuilder;

        public TestEntity(String string, StringBuilder builder) {
            mString = string;
            mBuilder = builder;
        }

        public StringBuilder getBuilder() {
            return mBuilder;
        }
    }

    public static StringBuilder test() {
        TestEntity entity = new TestEntity("hello", new StringBuilder("world"));
        try {
            return entity.getBuilder();
        } catch (Exception e) {
            return null;
        } finally {
            entity.getBuilder().append(" world");
            System.out.println("Finally block");
        }
    }

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

應該已經猜到結果了

Finally block
world world

原因上面都提到了,StringBuilder是引用類型並且是可變的,所以finally內的修改會生效.

結束~

有不足的地方歡迎指出,另外建了個新手交流Android開發的QQ羣,歡迎加入.

羣號:375276053

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章