首先,让我们来问你一个问题:finally 声明块将运行?

很多人认为 finally 语句块是一定要运行。其中还包括了一些非常有经验的 Java 程序猿。不幸的是,没有像很多像人们想象,对于这个问题,当然,答案是否定的,让我们来看看这个样品之后。

清点1

 public class Test {
public static void main(String[] args) {
System.out.println("return value of test(): " + test());
} public static int test() {
int i = 1;
// if(i == 1)
// return 0;
System.out.println("the previous statement of try block");
i = i / 0; try {
System.out.println("try block"); return i;
} finally {
System.out.println("finally block");
}
} }

清单 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)

另外。假设去掉上例中被凝视的两条语句前的凝视符,运行结果则是:

return value of test(): 0

在以上两种情况下。finally 语句块都没有运行,说明什么问题呢?

仅仅有与 finally 相相应的 try 语句块得到运行的情况下。finally 语句块才会运行。

以上两种情况,都是在 try 语句块之前返回(return)或者抛出异常,所以 try 相应的 finally 语句块没有运行。

那好,即使与 finally 相相应的 try 语句块得到运行的情况下,finally 语句块一定会运行吗?

请看以下这个样例(清单 2)。

清单2

public class Test {
public static void main(String[] args) {
System.out.println("return value of test(): " + test());
} public static int test() {
int i = 1; try {
System.out.println("try block");
System.exit(0); return i;
} finally {
System.out.println("finally block");
}
}
}

清单 2 的运行结果例如以下:

try blocktry block

finally 语句块还是没有运行,为什么呢?

由于我们在 try 语句块中执行了 System.exit (0) 语句。终止了 Java 虚拟机的执行。

 

那有人说了。在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。

OK !没有问题,我们不调用 System.exit(0) 这种方法,那么 finally 语句块就一定会运行吗?

答案还是否定的。



当一个线程在运行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed)。与其相相应的 finally 语句块可能不会运行。

还有更极端的情况,就是在线程执行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。

可能有人觉得死机、断电这些理由有些强词夺理,没有关系,我们仅仅是为了说明这个问题。

finally 语句演示样例说明

以下,我们先来看一个简单的样例(清单 3)。

清单 3.

 public class Test {
public static void main(String[] args) {
try {
System.out.println("try block"); return;
} finally {
System.out.println("finally block");
}
}
}

清单 3 的运行结果为:

try block
finally block

清单 3 说明 finally 语句块在 try 语句块中的 return 语句之前运行



 

我们再来看还有一个样例

清单 4.

public class Test {
public static void main(String[] args) {
System.out.println("reture value of test() : " + test());
} public static int test() {
int i = 1; try {
System.out.println("try block");
i = 1 / 0; return 1;
} catch (Exception e) {
System.out.println("exception block"); return 2;
} finally {
System.out.println("finally block");
}
}
}

清单 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 的控制权是在当前方法内转移。

清单 5.

public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}

清单 5 的运行结果:

return value of getValue(): 1

清单 6. 
  public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} public static int getValue() {
int i = 1; try {
return i;
} finally {
i++;
}
}
}

清单 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 所看到的:

图 1. getValue()函数正常运行的情况

由上图,我们能够清晰的看出,在 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)。

图 2. getValue()函数异常运行的情况



清单 7.

  public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} @SuppressWarnings("finally")
public static int getValue() {
int i = 1; try {
i = 4;
} finally {
i++; return i;
}
}
}

清单 7 的运行结果:

return value of getValue(): 5

清单 8.

public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} public static int getValue() {
int i = 1; try {
i = 4;
} finally {
i++;
} return i;
}
}

清单 8 的运行结果:

return value of getValue(): 5

让我们再来看一个略微复杂一点的样例 – 清单 9。

清单 9.

public class Test {
public static void main(String[] args) {
System.out.println(test());
} public static String test() {
try {
System.out.println("try block"); return test1();
} finally {
System.out.println("finally block");
}
} public static String test1() {
System.out.println("return statement"); return "after return";
}
}



清单 9 的结果:

try block
return statement
finally block
after return

return test1();这条语句等同于 :

String tmp = test1(); return tmp;

这项。应当清楚为什么结果所见上方的栏。

版权声明:本文博主原创文章。博客,未经同意不得转载。

左右 Java 于 finally 深度分析语句块的更多相关文章

  1. JAVA CAS原理深度分析 volatile,偏向锁,轻量级锁

    JAVA CAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 偏向锁,轻量级锁 https://blog.csdn.net/zqz ...

  2. 关于Java 值传递深度分析

    首先说观点:java只有值传递没有引用传递 然后再来看看值传递与引用传递两者的定义 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改, ...

  3. java面向对象值类属语句块

    在我们之前学习语句的时候,我们讲过一种比较特殊的语句块,那就是局部代码块.局部代码块的作用是什么呢,就是把临时使用的变量放在里面,之后执行完之后,局部代码块中定义的变量会直接被释放,这样就避免了那些我 ...

  4. JAVA CAS原理深度分析-转载

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  5. 【转】JAVA CAS原理深度分析

    java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...

  6. JAVA CAS原理深度分析

    参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...

  7. Java类加载机制深度分析

    转自:http://my.oschina.net/xianggao/blog/70826 参考:http://www.ibm.com/developerworks/cn/java/j-lo-class ...

  8. JAVA CAS原理深度分析(转)

    看了一堆文章,终于把JAVA CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...

  9. 转:JAVA CAS原理深度分析

    看了一堆文章,终于把Java CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...

随机推荐

  1. 依赖注入(DI)

    依赖注入(DI)   IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用“好莱坞原则”是应用程序以被动的方式实现对流程的定制.我们可以采用若干设 ...

  2. Android该HTTP下载

    今天学习了Android开发中比較难的一个环节,就是断点续传下载,非常多人看到这个标题就感觉头大.的确,假设没有良好的逻辑思维,这块的确非常难搞明确.以下我就将自己学到的知识和一些见解写下供那些在这个 ...

  3. Hdu 5073 Galaxy 精度问题

    思路: 其实求解很简单直接说解法,移动K个后 上下的角动量最小,能肯定是相连的(n-k)个,至于为什么 你自己好好想想(easy): 对于一些等质量的质点中心在 所在位置和除以点的个数 average ...

  4. Python 的PyCurl模块使用

    PycURl是一个C语言写的libcurl的python绑定库.libcurl 是一个自由的,并且容易使用的用在客户端的 URL 传输库.它的功能很强大,PycURL 是一个非常快速(参考多并发操作) ...

  5. cocos2d触摸事件处理机制(2.x和3.x变化)

    2.x的触摸事件的版本号 触摸事件处理有2种子.以下单点触摸的样本.(另一种多点触摸屏). 创建cocos2d 该项目. 1. 重写下面虚函数. bool ccTouchBegan(cocos2d:: ...

  6. 怎样在Android开发中FPS游戏实现的两种方式比较

    怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...

  7. Java 中泛型的全面解析(转)

    Java泛型(generics) 是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter).声明的类型参数在使用时用具体的类型来替换.泛型最主要的应用是在J ...

  8. 使用crontab创建 linux 系统定时任务#

    任务1: 每隔1分钟,运行一次 /home/sn/yeelink.sh文件 ,用于上传数据到www.yeelink.net 1. 先在当时目录里面创建一个cronfile文件 vim cronfile ...

  9. 在borland c++ builder 中使用 google test (gtest)

    google test version: 1.6 c++ builder version: xe6 1 download google test 1.6 2 unzip the zip file. T ...

  10. Windows 7 USB DVD Download Tool 制作的U盘无法启动安装Windows7 SP1

    以前用此工具安装Windows7一直正常,未遇到不能启动安装的问题.Windows7 SP1出来后,用此工具制作安装多台机器均不能引导安装(品牌机和兼容机均是如此 ),要么停留在光标闪烁的状态,要么就 ...