• JAVA finally字句的异常丢失和返回值覆盖解析


    转载:http://blog.csdn.net/sureyonder/article/details/5560538

    Java虚拟机在每个try语句块和与其相关的catch子句的结尾 处都会“调用”finally子句的子例程。实际上,finally子句在方法内部的表现很象“微型子例程”。finally子句正常结束后-指的是finally子句中最后一条语句正常执行完毕,不包括抛出异常,或执行return、continue、break等情况,隶属于这个finally子句的微型子例程执行“返回”操作。程序在第一次调用微型子例程的地方继续执行后面的语句。

    finally“微型子例程”不等同于方法函数的调用,finally子句都是在同一个栈内执行的,微型子例程的“返回”操作也不会涉及到方法退栈,仅仅是使程序计数器pc跳转到同一个方法的一个不同的位置继续执行。

    一 异常丢失

    [java] view plaincopy
     
    1. public static void exceptionLost()  
    2.  {  
    3.    try  
    4.    {  
    5.      try  
    6.      {  
    7.        throw new Exception( "exception in try" );  
    8.      }  
    9.      finally  
    10.      {  
    11.        throw new Exception( "exception in finally" );  
    12.      }  
    13.    }  
    14.    catch( Exception e )  
    15.    {  
    16.      System.out.println( e );  
    17.    }  
    18.  }  

    exceptionLost()的输出结果是“exception in finally”,而不是try块中抛出的异常,这是JAVA异常机制的一个瑕疵-异常丢失。

    在字节码中,throw语句不是原子性操作。在较老的JDK中,exceptionLost()中try块的throw语句分解为几步操作:


    1) 把Exception("exception in try")对象引用存储到一个局部变量中
      astore_2  // pop the reference to the thrown exception, store into local variable 2
    2) 调用finally微型子程序
    3) 把局部变量中的Exception("exception in try")对象引用push到操作数栈顶,然后抛出异常
      aload_2  // push the reference to the thrown exception from local variable 2

      athrow   // throw the exception

    如果finally通过break、return、continue,或者抛出异常而退出,那么上面的第3步就不会执行。

    在JDK1.6中,通过字节码我们可以看到,finally子句作为一种特殊的catch来实现的,下面是exceptionLost()方法的异常表:


    Exception table:
      from   to   target  type
       0     10    10     any
     0     21    21     Class java/lang/Exception

    finally可以捕获从0行到9行之间抛出的任何类型(any)的异常,并重新抛出捕获的异常,或者抛出一个自己构造的新异常,这个新异常就会覆盖try语句块中的异常。

    二 返回值覆盖

    [java] view plaincopy
     
    1. public static int getValue()  
    2.  {  
    3.    int value = 0;  
    4.      
    5.    try  
    6.    {  
    7.      value = 100;  
    8.        
    9.      return value;  
    10.    }  
    11.    finally  
    12.    {  
    13.      value = 200;  
    14.    }  
    15.  }  

    这个方法的返回值是100还是200?结果是100。
    在字节码中,return语句不是原子性操作,它会把getValue()中的return语句分解为几步操作:
    1) 把value值存储到一个局部变量(这里命名为temp)中:
       iload_0   // push local variable 0 - the 100
       istore_2   //  pop an int (the 100), store into local varaible 2
    2) 调用finally微型子程序
    3) 把局部变量(指temp)的值push到操作数栈顶,然后返回到调用方法
         iload_2  // push local varaible 2 - the 100
       ireturn      // return int on top of the stack - the 100: return 100

    由于return语句在返回之前会把返回值保存到一个临时的局部变量中,所以在finally子句内对value重新赋值不会影响返回值。

    了解finally子句内在的一些知识,我们能够了解finally能够做什么和不能够做什么,这样会帮助我们正确使用finally子句。

  • 相关阅读:
    线上一次大量 CLOSE_WAIT 复盘
    etcd 性能优化实践
    Web 前端密码加密是否有意义?
    tmp
    京东 PC 首页 2019 改版前端总结 原创: 何Jason,EC,小屁 凹凸实验室 今天
    http://stblog.baidu-tech.com/?p=1684) coredump调试记录
    Java字节码增强探秘
    dedecms 织梦更改rss的路径、网站地图sitemap的路径
    dedecms时间日期标签大全
    织梦CMS被挂马特征汇总
  • 原文地址:https://www.cnblogs.com/lanhzbupt/p/4074127.html
Copyright © 2020-2023  润新知