1. 程式人生 > >{轉載}java Finally塊中程式碼什麼時候執行

{轉載}java Finally塊中程式碼什麼時候執行

問題描述:try{}裡有一個return語句,那麼緊跟在這個try{}後面的finally{}中的程式碼是否會被執行?如果會的話,什麼時候被執行,在return之前還是return之後?

在Java語言的異常處理中,finally塊的作用就是為了保證無論出現什麼情況,finally塊裡的程式碼一定會被執行。由於程式執行return就意味著結束對當前函式的呼叫並跳出這個函式體,因此任何語句要執行都只能在return前執行(除非碰到exit函式),因此finally塊裡的程式碼也是在return之前執行的。此外,如果try-finally或者catch-finally中都有return,那麼finally塊中的return將會覆蓋別處的return語句,最終返回到呼叫者那裡的是finally中return的值。下面通過一個例子來說明這個問題:

package com.js;
/**
 * try-catch中有return語句,finally中程式碼執行時機問題
 * @author jiangshuai
 */
 
public class Test{
	public static int testFinally(){
		try {
			return 1;
		} catch (Exception e) {
			return 0;
		}finally{
			System.out.println("execute finally");
		}
	}
	public static void main(String[] args){
		int result = testFinally();
		System.out.println(result);
	}
}
執行結果:
execute finally
1
從上面這個例子中可以看出,在執行return語句前確實執行了finally塊中的程式碼。緊接著,在finally塊裡放置個return語句,來看看到底最終返回的是哪個return語句的值,例子如下圖所示:


package com.js;
/**
 * try-catch中有多個return語句,研究return的是哪一個
 * @author jiangshuai
 */
 
public class Test{
	public static int testFinally(){
		try {
			return 1;
		} catch (Exception e) {
			return 0;
		}finally{
			System.out.println("execute finally");
			return 3;
		}
	}
	public static void main(String[] args){
		int result = testFinally();
		System.out.println(result);
	}
}

執行結果:

execute finally
3

從以上執行結果可以看出,當finally塊中有return語句時,將會覆蓋函式中其他return語句。此外,由於在一個方法內部定義的變數都儲存在棧中,當這個函式結束後,其對應的棧就會被回收,此時在其方法體中定義的變數將不存在了,因此,對基本型別的資料,在finally塊中改變return的值對返回值沒有任何影響,而對引用型別的資料會有影響(詳見

Java中的值傳遞與引用傳遞詳解 )。下面通過一個例子來說明這個問題:

package com.js;
/**
 * 在finally塊中改變基本資料型別、引用型別對比
 * @author jiangshuai
 */
 
public class Test{
	public static int testFinally1(){
		int result = 1;
		try {
			result = 2;
			return result;
		} catch (Exception e) {
			return 0;
		}finally{
			result = 3;
			System.out.println("execute finally1");
		}
	}
	public static StringBuffer testFinally2(){
		StringBuffer s = new StringBuffer("Hello");
		try {
			return s;
		} catch (Exception e) {
			return null;
		}finally{
			s.append(" World");
			System.out.println("execute finally2");
		}
	}
	public static void main(String[] args){
		int result = testFinally1();
		System.out.println(result);
		StringBuffer resultRef = testFinally2();
		System.out.println(resultRef);
	}
}

執行結果:

execute finally1
2
execute finally2
Hello World

程式在執行到return時會首先將返回值儲存在一個指定的位置,其次去執行finally塊,最後再返回。在方法testFinally1中呼叫return前,先把result的值1儲存在一個指定的位置,然後再去執行finally塊中的程式碼,此時修改result的值將不會影響到程式的返回結果。testFinally2中,在呼叫return前先把s儲存到一個指定的位置,由於s為引用型別,因此在finally中修改s將會修改程式的返回結果。

引申:出現在Java程式中的finally塊是不是一定會被執行?

答案:不一定。

下面給出兩個finally塊不會被執行的例子:

1)、當程式進入try塊之前就出現異常時,會直接結束,不會執行finally塊中的程式碼,示例如下:

package com.js;
/**
 * 在try之前發生異常
 * @author jiangshuai
 */
 
public class Test{
	public static void testFinally1(){
		int result = 1/0;
		try {
			System.out.println("try block");
		} catch (Exception e) {
			System.out.println("catch block");
		}finally{
			System.out.println("finally block");
		}
	}
	public static void main(String[] args){
		testFinally1();
	}
}

執行結果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.js.Test.testFinally1(Test.java:9)
at com.js.Test.main(Test.java:19)

程式在執行1/0時會丟擲異常,導致沒有執行try塊,因此finally塊也就不會被執行。

2)、當程式在try塊中強制退出時也不會去執行finally塊中的程式碼,示例如下:

package com.js;
/**
 * 在try之前發生異常
 * @author jiangshuai
 */
 
public class Test{
	public static void testFinally1(){
		try {
			System.out.println("try block");
			System.exit(0);
		} catch (Exception e) {
			System.out.println("catch block");
		}finally{
			System.out.println("finally block");
		}
	}
	public static void main(String[] args){
		testFinally1();
	}
}

執行結果:

try block

上例在try塊中通過呼叫System.exit(0)強制退出了程式,因此導致finally塊中的程式碼沒有被執行。