首先来问你们一个问题:finally 语句块必定会执行吗?java
不少人都认为 finally 语句块是确定要执行的,其中也包括一些颇有经验的 Java 程序员。惋惜并不像大多人所认为的那样,对于这个问题,答案固然是否认的,咱们先来看下面这个例子。程序员
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 // if(i == 1) 9 // return 0; 10 System.out.println("the previous statement of try block"); 11 i = i / 0; 12 13 try { 14 System.out.println("try block"); 15 16 return i; 17 } finally { 18 System.out.println("finally block"); 19 } 20 } 21 22 } 23
清单 1 的执行结果以下:函数
the previous statement of try block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.bj.charlie.Test.test(Test.java:15) at com.bj.charlie.Test.main(Test.java:6) |
另外,若是去掉上例中被注释的两条语句前的注释符,执行结果则是:spa
return value of test(): 0 |
在以上两种状况下,finally 语句块都没有执行,说明什么问题呢?
只有与 finally 相对应的 try 语句块获得执行的状况下,finally 语句块才会执行。
以上两种状况,都是在 try 语句块以前返回(return)或者抛出异常,因此 try 对应的 finally 语句块没有执行。线程
那好,即便与 finally 相对应的 try 语句块获得执行的状况下,finally 语句块必定会执行吗?
请看下面这个例子(清单 2)。code
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of test(): " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 System.exit(0); 12 13 return i; 14 } finally { 15 System.out.println("finally block"); 16 } 17 } 18 }
清单 2 的执行结果以下:ci
try block |
finally 语句块仍是没有执行,为何呢?
由于咱们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。
那有人说了,在通常的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。
OK !没有问题,咱们不调用 System.exit(0) 这个方法,那么 finally 语句块就必定会执行吗?get
答案仍是否认的。
当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。
还有更极端的状况,就是在线程运行 try 语句块或者 catch 语句块时,忽然死机或者断电,finally 语句块确定不会执行了。
可能有人认为死机、断电这些理由有些强词夺理,没有关系,咱们只是为了说明这个问题。
下面,咱们先来看一个简单的例子(清单 3)。
1 public class Test { 2 public static void main(String[] args) { 3 try { 4 System.out.println("try block"); 5 6 return; 7 } finally { 8 System.out.println("finally block"); 9 } 10 } 11 }
清单 3 的执行结果为:
try block finally block |
清单 3 说明 finally 语句块在 try 语句块中的 return 语句以前执行
咱们再来看另外一个例子
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("reture value of test() : " + test()); 4 } 5 6 public static int test() { 7 int i = 1; 8 9 try { 10 System.out.println("try block"); 11 i = 1 / 0; 12 13 return 1; 14 } catch (Exception e) { 15 System.out.println("exception block"); 16 17 return 2; 18 } finally { 19 System.out.println("finally block"); 20 } 21 } 22 }
清单 4 的执行结果为:
try block exception block finally block reture value of test() : 2 |
清单 4 说明了 finally 语句块在 catch 语句块中的 return 语句以前执行。
从上面的清单 3 和清单 4,咱们能够看出,
其实 finally 语句块是在 try 或者 catch 中的 return 语句以前执行的。
更加通常的说法是,finally 语句块应该是在控制转移语句以前执行,控制转移语句除了 return 外,还有 break 和 continue。
另外,throw 语句也属于控制转移语句。虽然 return、throw、break 和 continue 都是控制转移语句,可是它们之间是有区别的。
其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 try { 8 return 0; 9 } finally { 10 return 1; 11 } 12 } 13 }
清单 5 的执行结果:
return value of getValue(): 1 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 return i; 11 } finally { 12 i++; 13 } 14 } 15 } 16
清单 6 的执行结果:
return value of getValue(): 1 |
利用咱们上面分析得出的结论:
finally 语句块是在 try 或者 catch 中的 return 语句以前执行的。
由此,能够轻松的理解清单 5 的执行结果是 1。
由于 finally 中的 return 1;语句要在 try 中的 return 0;语句以前执行,那么 finally 中的 return 1;
语句执行后,把程序的控制权转交给了它的调用者 main()函数,而且返回值为 1。
那为何清单 6 的返回值不是 2,而是 1 呢?
按照清单 5 的分析逻辑,finally 中的 i++;
语句应该在 try 中的 return i;以前执行啊?
i 的初始值为 1,那么执行 i++;以后为 2,再执行 return i;那不就应该是 2 吗?怎么变成 1 了呢?
咱们来分析一下其执行顺序:分为正常执行(没有 exception)和异常执行(有 exception)两种状况。咱们先来看一下正常执行的状况,如图 1 所示:
由上图,咱们能够清晰的看出,在 finally 语句块(iinc 0, 1)执行以前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成这个任务的指令是 istore_1;而后执行 finally 语句块(iinc 0, 1),finally 语句块把位于 0 这个位置的本地变量表中的值加 1,变成 2;待 finally 语句块执行完毕以后,把本地表量表中 1 的位置上值恢复到操做数栈(iload_1),最后执行 ireturn 指令把当前操做数栈中的值(1)返回给其调用者(main)。这就是为何清单 6 的执行结果是 1,而不是 2 的缘由。
再让咱们来看看异常执行的状况。是否是有人会问,你的清单 6 中都没有 catch 语句,哪来的异常处理呢?我以为这是一个好问题,其实,即便没有 catch 语句,Java 编译器编译出的字节码中仍是有默认的异常处理的,别忘了,除了须要捕获的异常,还可能有不需捕获的异常(如:RunTimeException 和 Error)。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 @SuppressWarnings("finally") 7 public static int getValue() { 8 int i = 1; 9 10 try { 11 i = 4; 12 } finally { 13 i++; 14 15 return i; 16 } 17 } 18 }
清单 7 的执行结果:
return value of getValue(): 5 |
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("return value of getValue(): " + getValue()); 4 } 5 6 public static int getValue() { 7 int i = 1; 8 9 try { 10 i = 4; 11 } finally { 12 i++; 13 } 14 15 return i; 16 } 17 }
清单 8 的执行结果:
return value of getValue(): 5 |
让咱们再来看一个稍微复杂一点的例子 – 清单 9。
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println(test()); 4 } 5 6 public static String test() { 7 try { 8 System.out.println("try block"); 9 10 return test1(); 11 } finally { 12 System.out.println("finally block"); 13 } 14 } 15 16 public static String test1() { 17 System.out.println("return statement"); 18 19 return "after return"; 20 } 21 }
清单 9 的结果:
try block return statement finally block after return |
return test1();这条语句等同于 :
String tmp = test1(); return tmp; |
这样,就应该清楚为何是上面所示的执行结果了吧!