六個例子徹底理解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