Java栈和局部变量操作
Java虚拟机是基于栈的机器,几乎所有Java虚拟机的指令都与操作数栈相关。栈操作包括把常量压入操作数栈、执行通用的栈操作、在操作数栈和局部变量之间往返传输值。
1常量入栈操作:
操作码在执行常量入栈操作之前,使用三种方式指明常量的值:常量值隐含包含在操作码内部、常量值在字节码中如同操作数一样跟随在操作码之后,或者从常量池中取出常量。
1.1常量值隐含包含在操作码内部:
将一个字长的常量压入栈
操作码 |
操作数 |
说明 |
---|---|---|
iconst_m1 |
(无) |
将int类型值-1压入栈 |
iconst_0 |
(无) |
将int类型值0压入栈 |
iconst_1 |
(无) |
将int类型值1压入栈 |
iconst_2 |
(无) |
将int类型值2压入栈 |
iconst_3 |
(无) |
将int类型值3压入栈 |
iconst_4 |
(无) |
将int类型值4压入栈 |
iconst_5 |
(无) |
将int类型值5压入栈 |
fconst_0 |
(无) |
将float类型值0压入栈 |
fconst_1 |
(无) |
将float类型值1压入栈 |
fconst_2 |
(无) |
将float类型值2压入栈 |
将两个字长的常量压入栈
操作码 |
操作数 |
说明 |
---|---|---|
lconst_0 |
(无) |
将long类型值0压入栈 |
lconst_1 |
(无) |
将long类型值1压入栈 |
dconst_0 |
(无) |
将double类型值0压入栈 |
dconst_1 |
(无) |
将double类型值1压入栈 |
给一个对象引用赋空值时会用到aconst_null指令
将空(null)对象引用压入栈
操作码 |
操作数 |
说明 |
---|---|---|
aconst_null |
(无) |
将空(null)对象引用压入栈 |
例如下面代码:
publicclassStackTest {
/**
* @paramargs
*/
publicstaticvoidmain(String[] args) {
//TODOAuto-generated method stub
inti = 0;
intj = 4;
intk;
k= i + j;
floata = 0;
floatb = 1;
floatc =a + b;
longx = 0;
longy = 1;
longz =x + y;
Stringstring= null;
}
}
用javap工具查看其字节码为:
Compiledfrom "StackTest.java"
publicclass StackTest extends java.lang.Object{
publicStackTest();
Code:
0: aload_0
1: invokespecial #8;//Method java/lang/Object."<init>":()V
4: return
publicstatic void main(java.lang.String[]);
Code:
0: iconst_0 //常量int类型的0入栈
1: istore_1 //弹出栈顶元素0存入位置1的局部变量中
2: iconst_4 //常量int类型的4入栈
3: istore_2 //弹出栈顶元素4存入位置2的局部变量中
4: iload_1 //从位置为1的局部变量中取出元素int类型的0压入栈
5: iload_2 //从位置为2的局部变量中取出元素int类型的4压入栈
6: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
7: istore_3 //弹出栈顶元素4存入位置为3的局部变量中
8: fconst_0 //常量float类型的0入栈
9: fstore 4 //弹出栈顶元素0存入位置为4的局部变量中
11: fconst_1 //常量float类型的1入栈
12: fstore 5 //弹出栈顶元素1存入位置为5的局部变量中
14: fload 4 //从位置为4的局部变量中取出元素float类型的0压入栈
16: fload 5 //从位置为5的局部变量中取出元素float类型的1压入栈
18: fadd //从栈顶弹出两个元素然后做加法,把结果压入栈
19: fstore 6 //弹出栈顶元素1存入位置为3的局部变量中
21: lconst_0 //常量long类型的0入栈
22: lstore 7 // 弹出栈顶元素0存入位置为7和8的局部变量中
24: lconst_1 //常量long类型的1入栈
25: lstore 9 // 弹出栈顶元素0存入位置为9和10的局部变量中
27: lload 7 //从位置为7和8的局部变量中取出元素long类型的0压入栈
29: lload 9 //从位置为9和10的局部变量中取出元素long类型的1压入栈
31: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈
32: lstore 11 //弹出栈顶元素1存入位置为11和12的局部变量中
34: aconst_null //将null对象引用压入栈
35: astore 13 //弹出栈顶元素null存入位置为13的局部变量中
37: return
}
1.2常量值在字节码中跟随在操作码之后:
将byte和short类型常量压入栈
操作码 |
操作数 |
说明 |
---|---|---|
bipush |
一个byte类型的数 |
将byte类型的数转换为int类型的数,然后压入栈 |
sipush |
一个short类型的数 |
将short类型的数转换为int类型的数,然后压入栈 |
1.3从常量池中取出常量
操作码 |
操作数 |
说明 |
---|---|---|
ldc |
无符号8位数indexbyte |
从由indexbyte指向的常量池入口中取出一个字长的值,然后将其压入栈 |
ldc_w |
无符号16位数indexshort |
从由indexshort指向的常量池入口中取出一个字长的值,然后将其压入栈 |
ldc2_w |
无符号16位数indexshort |
从由indexshort指向的常量池入口中取出两个字长的值,然后将其压入栈 |
这三个操作码是从常量池中取出常量,然后将其压入栈,这些操作码的操作码表示常量池索引,Java虚拟机通过给定的索引查找相应的常量池入口,决定这些常量的类型和值,并把它们压入栈。
常量池索引是一个无符号值,ldc和ldc_w是把一个字长的项压入栈,区别在于:ldc的索引只有一个8位,只能指向常量池中1~255范围的位置。ldc_w的索引有16位,可以指向1~65535范围的位置。
例如下面代码:
publicclassStackTest {
/**
* @paramargs
*/
publicstaticvoidmain(String[] args) {
//TODOAuto-generated method stub
bytei = 125;
bytej = -128;
int k =i + j;
shorta = 32767;
shortb = - 32768;
intc =a + b;
int x = 2147483647;
inty = -2147483648;
intz =x + y;
longI = 2147483648L;
longJ = -2147483649L;
longK =I + J;
}
}
用javap工具查看其字节码为:
Compiledfrom "StackTest.java"
publicclass StackTest extends java.lang.Object{
publicStackTest();
Code:
0: aload_0
1: invokespecial #8;//Method java/lang/Object."<init>":()V
4: return
publicstatic void main(java.lang.String[]);
Code:
0: bipush 125 //将byte类型的255转换成int类型压入栈
2: istore_1 //弹出栈顶元素255存入位置为1的局部变量中
3: bipush -128//将byte类型的-128转换成int类型压入栈
5: istore_2 //弹出栈顶元素-128存入位置为2的局部变量中
6: iload_1 //取出位置为1的局部变量中的数压入栈
7: iload_2 //取出位置为2的局部变量中的数压入栈
8: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
9: istore_3 //弹出栈顶元素存入位置为3的局部变量中
10: sipush 32767//将short类型的32767转换成int类型压入栈
13: istore 4 //弹出栈顶元素32767存入位置为4的局部变量中
15: sipush -32768/将short类型的-32768转换成int类型压入栈
18: istore 5 //弹出栈顶元素-32768存入位置为5的局部变量中
20: iload 4 //取出位置为4的局部变量中的数压入栈
22: iload 5 //取出位置为5的局部变量中的数压入栈
24: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
25: istore 6 /弹出栈顶元素存入位置为6的局部变量中
27: ldc #16;//int 2147483647 //从常量池索引16的位置取出2147483647压入栈
29: istore 7 //弹出栈顶元素2147483647存入位置为4的局部变量中
31: ldc #17;//int -2147483648 //从常量池索引17的位置取出-2147483648压入栈
33: istore 8 //弹出栈顶元素-2147483648存入位置为8的局部变量中
35: iload 7 //取出位置为7的局部变量中的数压入栈
37: iload 8 //取出位置为8的局部变量中的数压入栈
39: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
40: istore 9 //弹出栈顶元素存入位置为9的局部变量中
42: ldc2_w #18;//long 2147483648l //从常量池索引18的位置取出long类型的2147483648L压入栈
45: lstore 10 //弹出栈顶元素2147483648L存入位置为10和11的局部变量中
47: ldc2_w #20;//long -2147483649l //从常量池索引20的位置取出long类型的-2147483649L压入栈
50: lstore 12 //弹出栈顶元素-2147483649L存入位置为12和13的局部变量中
52: lload 10 //取出位置为10和11的局部变量中的数压入栈
54: lload 12 //取出位置为12和13的局部变量中的数压入栈
56: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈
57: lstore 14 //弹出栈顶元素存入位置为14和15的局部变量中
59: return
}