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

貌似不少人对下面的方法的返回值都比较迷糊:java

Java代码jvm

  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.ui

 

为何?this

用javap -verbose ReturnValueTest 查看字节码:spa

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

Javap代码ci

  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并不影响返回值.get

相关文章
相关标签/搜索