码迷,mamicode.com
首页 > 其他好文 > 详细

用bytecode来看try-catch-finally和return

时间:2014-10-19 23:18:20      阅读:225      评论:0      收藏:0      [点我收藏+]

标签:异常   字节码   

之前看过一篇关于returnfinally执行顺序的文章,仅在Java的语言层面做了分析,其实我倒觉得直接看bytecode可能来的更清晰一点。

最近一直在看Java虚拟机规范,发现直接分析bytecode更能加深对Java语言的理解。

先看一个只有try-finally,没有catch的例子。

try - finally

public class ExceptionTest {
  public void tryFinally() {
    try {
      tryItOut();
    } finally {
      wrapItUp();
    }
  }


  // auxiliary methods
  public void tryItOut() { }

  public void wrapItUp() {}
}

通过javap -c ExceptionTest来查看它的字节码。

public void tryFinally();
  Code:
     0: aload_0
     1: invokevirtual #2  // Method tryItOut:()V
     4: aload_0
     5: invokevirtual #3  // Method wrapItUp:()V
     8: goto          18
    11: astore_1
    12: aload_0
    13: invokevirtual #3  // Method wrapItUp:()V
    16: aload_1
    17: athrow
    18: return
  Exception table:
     from    to  target type
         0     4    11   any

如果没有抛出异常,那么它的执行顺序为

0: aload_0
1: invokevirtual #2  // Method tryItOut:()V
4: aload_0
5: invokevirtual #3  // Method wrapItUp:()V
18: return

如果抛出了异常,JVM会在

Exception table:
   from    to  target type
       0     4    11   any

中进行控制跳转。如果是位于0到4字节之间的命令抛出了任何类型(any type)的异常,会跳转到11字节处继续运行。

11: astore_1
12: aload_0
13: invokevirtual #3
16: aload_1
17: athrow

astore_1会把抛出的异常对象保存到local variable数组的第二个元素。下面两行指令用来调用成员方法wrapItUp

12: aload_0
13: invokevirtual #3

最后通过

16: aload_1
17: athrow

重新抛出异常。

通过以上分析可以得出结论

在try-finally中,try块中抛出的异常会首先保存在local variable中,然后执行finally块,执行完毕后重新抛出异常。


如果我们把代码修改一下,在try块中直接return。

try - return - finally

public void tryFinally() {
  try {
    tryItOut();
    return;
  } finally {
    wrapItUp();
  }
}

”反汇编“一下:

 0: aload_0
 1: invokevirtual #2 // Method tryItOut:()V
 4: aload_0
 5: invokevirtual #3 // Method wrapItUp:()V
 8: return
 9: astore_1
10: aload_0
11: invokevirtual #3 // Method wrapItUp:()V
14: aload_1
15: athrow

可以看出finally块的代码仍然被放到了return之前。

如果try块中有return statement,一定是finally中的代码先执行,然后return。

JVM规范是这么说的

Compilation of a try-finally statement is similar to that of try-catch. Pior to transferring control outside the try statement, whether that transfer is normal or abrupt, because an exception has been thrown, the finally clause must first be execute.


try - catch - finally

给上面的代码加一个catch块

public void tryCatchFinally() {
  try {
    tryItOut();
  } catch (TestExc e) {
    handleExc(e);
  } finally {
    wrapItUp();
  }
}

javap一下

public void tryCatchFinally();
  Code:
     0: aload_0
     1: invokevirtual #2
     4: aload_0
     5: invokevirtual #3
     8: goto          31
    11: astore_1
    12: aload_0
    13: aload_1
    14: invokevirtual #5                  
    17: aload_0
    18: invokevirtual #3
    21: goto          31
    24: astore_2
    25: aload_0
    26: invokevirtual #3
    29: aload_2
    30: athrow
    31: return
Exception table:
   from    to  target type
       0     4    11   Class TestExc
       0     4    24   any
      11    17    24   any

通过Exception table可以看出:

  • catch监听 0 ~ 4 字节类型为TextExc的异常。
  • finally为 0 ~ 4 以及 11 ~ 17 字节任何类型的异常。

也就说 catch block 本身也在 finally block 的管辖范围之内。如果 catch block 中有 return statement,那么也一定是在 finally block 之后执行。


查看原文 http://www.liangfeizc.com/blog/article/32/

用bytecode来看try-catch-finally和return

标签:异常   字节码   

原文地址:http://blog.csdn.net/feelang/article/details/40273869

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!