从JVM指令层面看try-catch-finally返回值问题


 

貌似很多人对下面的方法的返回值都比较迷糊:

Java代码   收藏代码
  1. package cc.lixiaohui.demo;    
  2.     
  3. public class ReturnValueTest {    
  4.     public int test() {    
  5.         int a;    
  6.         try {    
  7.             a = 1;    
  8.             //int b = 1 / 0;    
  9.             return a;    
  10.         } catch (Exception e) {    
  11.             a = 2;    
  12.             return a;    
  13.         } finally {    
  14.             a = 3;    
  15.         }    
  16.     }    
  17. }  

 

test方法的返回值自然是1,如果把注释那行去掉,那就是2.

 

为什么?

用javap -verbose ReturnValueTest 查看字节码:

重点查看test()方法指令:

Javap代码   收藏代码
  1. Compiled from "ReturnValueTest.java"  
  2. public class cc.lixiaohui.demo.ReturnValueTest extends java.lang.Object  
  3.   SourceFile: "ReturnValueTest.java"  
  4.   minor version: 0  
  5.   major version: 49       
  6.   Constant pool:        --常量池  
  7. const #1 = class        #2;     //  cc/lixiaohui/demo/ReturnValueTest  
  8. const #2 = Asciz        cc/lixiaohui/demo/ReturnValueTest;  
  9. const #3 = class        #4;     //  java/lang/Object  
  10. const #4 = Asciz        java/lang/Object;  
  11. const #5 = Asciz        <init>;  
  12. const #6 = Asciz        ()V;  
  13. const #7 = Asciz        Code;  
  14. const #8 = Method       #3.#9;  //  java/lang/Object."<init>":()V  
  15. const #9 = NameAndType  #5:#6;//  "<init>":()V  
  16. const #10 = Asciz       LineNumberTable;  
  17. const #11 = Asciz       LocalVariableTable;  
  18. const #12 = Asciz       this;  
  19. const #13 = Asciz       Lcc/lixiaohui/demo/ReturnValueTest;;  
  20. const #14 = Asciz       test;  
  21. const #15 = Asciz       ()I;  
  22. const #16 = class       #17;    //  java/lang/Exception  
  23. const #17 = Asciz       java/lang/Exception;  
  24. const #18 = Asciz       a;  
  25. const #19 = Asciz       I;  
  26. const #20 = Asciz       e;  
  27. const #21 = Asciz       Ljava/lang/Exception;;  
  28. const #22 = Asciz       SourceFile;  
  29. const #23 = Asciz       ReturnValueTest.java;  
  30.   
  31. {  
  32. public cc.lixiaohui.demo.ReturnValueTest();     --构造方法就不分析了  
  33.   Code:  
  34.    Stack=1, Locals=1, Args_size=1  
  35.    0:   aload_0  
  36.    1:   invokespecial   #8; //Method java/lang/Object."<init>":()V  
  37.    4:   return  
  38.   LineNumberTable:  
  39.    line 80  
  40.   
  41.   LocalVariableTable:  
  42.    Start  Length  Slot  Name   Signature  
  43.    0      5      0    this       Lcc/lixiaohui/demo/ReturnValueTest;  
  44.   
  45.   
  46. public int test();  
  47.   Code:  
  48.    Stack=1, Locals=5, Args_size=1 -- [Stack=1貌似表示栈深度为1,]Locals=5表示局部变量表长度为5, Args_size=1表示该方法有1个参数(this)  
  49.    0:   iconst_1        --将int值 1 压栈  
  50.    1:   istore_1        --将栈顶值(即1)弹出保存至局部变量表第2个位置(局部变量表下标是从0开始的,但是0位置被this变量占用了)  
  51.    2:   iload_1         --将局部变量表第2个位置的值压栈  
  52.    3:   istore  4       --将栈顶的值弹出并保存至局部变量表第5个位置(这里可以看到)  
  53.    5:   iconst_3        --(这里开始为finally块)将int值3压栈  
  54.    6:   istore_1        --将栈顶的值(即3)弹出并存储至局部变量表第2个位置  
  55.    7:   iload   4       --将局部变量表第5个位置(即1)压栈  
  56.    9:   ireturn         --返回栈顶的值(即1), 结束方法调用(该路径为try(未抛异常) -> finally)  
  57.      
  58.    10:  astore_2        --将栈顶的引用(这里即为catch块中捕捉到的异常e)存储至局部变量表的第3个位置  
  59.    11:  iconst_2        --将int值2压栈  
  60.    12:  istore_1        --将栈顶值(即2)弹出并存储至局部变量表第2个位置  
  61.    13:  iload_1         --将局部变量表第2个位置的值(即2)压栈  
  62.    14:  istore  4       --将栈顶值弹出(即2)并保存至局部变量表第5个位置,原来第五个位置是1,现在1被覆盖了,变为2  
  63.    16:  iconst_3        --(这里开始为finally块)将int值3压栈  
  64.    17:  istore_1        --将栈顶的值(即3)弹出并存储至局部变量表第2个位置  
  65.    18:  iload   4       --将局部变量表第5个位置(即2)压栈  
  66.    20:  ireturn         --返回栈顶的值(即2),结束方法调用(该路径为try(抛Exception或其子类异常) -> catch -> finally)  
  67.      
  68.    21:  astore_3        --将栈顶的引用(这里为非Exception的子类异常)存储至局部变量表的第4个位置  
  69.    22:  iconst_3        --将int值3压栈  
  70.    23:  istore_1        --将栈顶值(即3)弹出并存储至局部变量表第二个位置  
  71.    24:  aload_3         --将局部变量表第4个位置(即为异常引用)压栈  
  72.    25:  athrow          --将栈顶的异常抛出(该路径为try(抛非Exception或其子类异常) -> finally, 或者try(抛Exception或其子类异常) -> catch(抛任何异常) -> finally )  
  73.   Exception table:  
  74.    from   to  target type  
  75.      0     5    10   Class java/lang/Exception  --若执行到0-5行(即在try块中)抛出java.lang.Exception或其子类则跳转至第10行执行(即catch块)  
  76.      0     5    21   any                        --若执行到0-5行(即在try块中)抛出非java.lang.Exception或其子类则跳转至第21行执行(即finally块)  
  77.     10    16    21   any                        --若执行到10-16行(即在catch块中)抛出任何异常则跳转至21行执行(即finally块)  
  78.   LineNumberTable:      --这个表为源码行数与字节码行数的映射  
  79.    line 130  
  80.    line 142  
  81.    line 195  
  82.    line 147  
  83.    line 1510  
  84.    line 1611  
  85.    line 1713  
  86.    line 1916  
  87.    line 1718  
  88.    line 1821  
  89.    line 1922  
  90.    line 2024  
  91.   
  92.   LocalVariableTable:       --这个即为局部变量表, start和length结合起来就可以确定该变量的作用范围, 例如this作用范围为整个方法  
  93.    Start  Length  Slot  Name   Signature  
  94.    0      26      0    this       Lcc/lixiaohui/demo/ReturnValueTest;   --占用第1个slot(一个slot应该是32bits)  
  95.    2      8      1    a       I                                         --I标识int, 占用第2个slot  
  96.    13      8      1    a       I                                        --占用第2个slot  
  97.    24      2      1    a       I                                        --占用第2个slot  
  98.    11      10      2    e       Ljava/lang/Exception;                   --占用第3个slot  
  99.   
  100.   
  101. }  

 

可以发现jvm始终把返回值放在最后一个局部变量表的位置,而且在finally中改变x并不影响返回值.

 

参考:

JVM栈帧

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值