源码
下面代码的返回结果是1不是2,下面从字节码的角度分析一下原因
public class Start { | |
public static void main(String[] args) { | |
int demo = demo(); | |
System.out.println(demo); | |
} | |
public static int demo() { | |
int x = 1; | |
try { | |
return x; | |
} finally { | |
x = 2; | |
} | |
} | |
} |
代码字节码分析
获取方法的字节码命令为
javap -v xxx.class
上面代码的字节码部分如下图所示(其中红色的字为解析,下面会对详细内容进行解释)
首先要明确该段代码中有一个操作数栈和局部变量表,如下图所示
当程序执行 int x = 1,时会有两个操作,常量1会压入操作数栈栈顶,然后弹出栈顶元素赋值为局部变量表的x的位置,对应字节码中的0: iconst_1 , 1: istore_0 ,如下图所示
然后执行try-catch-finally中的方法,用到x,所以执行字节码 2: iload_0,命令,把局部变量表中的0号位置数据加载到操作数栈栈顶。如下图所示。
接下来执行 3: istore_1 方法,把操作数栈顶元素保存到局部变量表1号位置,如下图所示
接下来执行4: iconst_2 , 5: istore_0 字节码,即x = 2的代码,把2压入到操作数栈栈顶中,把栈顶元素保存到局部变量表0号位置中,如下图所示。
最后执行 6: iload_1 , 7: ireturn 字节码,把局部变量表1号位置上数据压入到操作数栈栈顶,返回操作数栈顶元素,如下图所示
源码拓展
如果代码想返回2,可以有如下操作
public static int demo() { | |
int x = 1; | |
try { | |
return x; | |
} finally { | |
x = 2; | |
return x; | |
} | |
} |
或者
public static int demo() { | |
int x = 1; | |
try { | |
} finally { | |
x = 2; | |
} | |
return x; | |
} |
字节码命令部分介绍
iconst_n
把n压入到操作数栈栈顶,n为表示具体的操作值,比如int x =5,则执行iconst_5命令。 不过也有其他的命令,当int取值-1~ 5采用iconst指令,取值-128~ 127采用bipush指令,取值-32768~ 32767采用sipush指令,取值-2147483648~2147483647采用 ldc 指令。
int x =1; | |
int y =11; |
对应的字节码如下图所示
istore_n
把操作数栈栈顶元素保存到局部变量表n号位置
iload_n
把局部变量表n号位置的数据压入到操作数栈栈顶
参考
https://mp.weixin.qq.com/s/4tBjAt-lTh6-zmhW-44S6A https://www.imooc.com/wenda/detail/440500