從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

相關文章
相關標籤/搜索