Java栈和局部变量操作(一)

时间:2021-04-11 01:14:38

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存入位置为78的局部变量中

24: lconst_1 //常量long类型的1入栈

25: lstore 9 // 弹出栈顶元素0存入位置为910的局部变量中

27: lload 7 //从位置为78的局部变量中取出元素long类型的0压入栈

29: lload 9 //从位置为910的局部变量中取出元素long类型的1压入栈

31: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈

32: lstore 11 //弹出栈顶元素1存入位置为1112的局部变量中

34: aconst_null //null对象引用压入栈

35: astore 13 //弹出栈顶元素null存入位置为13的局部变量中

37: return

}

1.2常量值在字节码中跟随在操作码之后:

byteshort类型常量压入栈

操作码

操作数

说明

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虚拟机通过给定的索引查找相应的常量池入口,决定这些常量的类型和值,并把它们压入栈。

常量池索引是一个无符号值,ldcldc_w是把一个字长的项压入栈,区别在于:ldc的索引只有一个8位,只能指向常量池中1255范围的位置。ldc_w的索引有16位,可以指向165535范围的位置。

例如下面代码:

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存入位置为1011的局部变量中

47: ldc2_w #20;//long -2147483649l //从常量池索引20的位置取出long类型的-2147483649L压入栈

50: lstore 12 //弹出栈顶元素-2147483649L存入位置为1213的局部变量中

52: lload 10 //取出位置为1011的局部变量中的数压入栈

54: lload 12 //取出位置为1213的局部变量中的数压入栈

56: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈

57: lstore 14 //弹出栈顶元素存入位置为1415的局部变量中

59: return