轉自:http://blog.csdn.net/ZangXT/archive/2009/05/21/4206672.aspx
標題:finally是肯定會執行的!
try/catch/finally語句下,finally子句是肯定會執行的。但是很多人做不同的測試,卻得出了不同的結論。
具體的原理最好是去看《深入java虛擬機器》,裡面對jsr、ret等幾個指令做了詳細的說明。這裡不深入分析,而僅僅是從表現形式上看一下finally的特徵。
代碼:
/*
* author: Zang XT
*/
public class TestFinally {
public static void main(String[] args) {
System.out.println("test1:" + testFinal1());
System.out.println("test2:" + testFinal2());
System.out.println("test3:" + testFinal3());
System.out.println("test4:" + testFinal4());
}
static int testFinal1() {
int i = 1;
try {
return i;
} finally {
System.out.println("in testFinal1():finally 肯定會被執行的!");
i = 48;
}
}
static String testFinal2() {
String str = "try";
try {
return str;
} finally {
System.out.println("in testFinal2():finally 肯定會被執行的!");
str = "finally";
}
}
static StringBuilder testFinal3() {
StringBuilder build = new StringBuilder("try ");
try {
return build;
} finally {
System.out.println("in testFinal3():finally 肯定會被執行的!");
build.append("finally");
build = new StringBuilder("你猜我是誰!");
}
}
static String testFinal4() {
try {
return "return in try";
} finally {
System.out.println("in testFinal4():finally 肯定會被執行的!");
return "return in finally";
}
}
}
輸出是:
in testFinal1():finally 肯定會被執行的!
test1:1
in testFinal2():finally 肯定會被執行的!
test2:try
in testFinal3():finally 肯定會被執行的!
test3:try finally
in testFinal4():finally 肯定會被執行的!
test4:return in finally
結論很明顯,finally的語句確實執行了,而且肯定是在方法return之前執行的,而且,如果finally中有return語句的話,方法直接結束。這裡需要注意的只有一點:在try中的return語句會將返回結果值壓棧,然後轉入到finally子過程,等到finally子過程執行完畢之後(沒有return),再返回。
下面具體看4個例子:
在testFinal1()中,return i;會將結果i的值,也就是1壓入棧。即使在finally中將i修改了(i=48),也不回對已經壓入棧裡的1造成任何影響。
在testFinal2()中,return str;將str的內容壓入棧,比如我們假設str的內容為0x108(只是一個地址值),通過這個地址值我們能找到"try",那棧裡的內容就是0x108。執行str = "finally",這時候str這個變數的內容可能變為0x237了,這是串"finally"的地址。方法調用結束後,返回的是什嗎?return時壓入棧裡的0x108。所以在列印結果時,我們列印的是通過0x108找到的字串"try"。
在testFinal3()中,return 壓棧的是build這個變數的值,比如是0x3579,通過這個值我們可以找到StringBuilder對象。finally語句塊中對這個對象的內容進行了修改。build = new StringBuilder("你猜我是誰!");讓build變數指向了一個新的對象,這時候build的值可能是0x4579了。但是,別忘了,原來的StringBuilder對象仍然在0x3579處,而我們壓棧的正是0x3579啊!方法返回後,我們得到的傳回值0x3579,通過這個引用值找到相應的StringBuilder對象,所以列印的結果是test3:try finally。
在testFinal4()中,finally有return語句,直接返回,方法結束。
為什麼不同的人有不同的結論?關鍵是沒有正確理解壓棧的是什麼東西。其實初學java的時候,如果理解了變數是什麼,並區分引用和對象本身就不會得到錯誤的結論了。再有,如果理解java中,方法調用都是採用傳值模式的話,這裡也就類似的可以明白了。