面试题目💬:
public class demo {
public static void main(String[] args) {
int a = 1;
a = a++;
int b = a++;
int c = a+ ++a * a++;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}
这道题是考察关于变量自增的题型
运行结果🧾:
a = 4
b = 1
c = 11
题目解析✏️:
int a = 1;
a = a++;
- 创建变量a,并对a赋值为1。此时局部变量表中a的值为1。
- a = a++; ++在变量后面所以++运算需要后运算,先将局部变量表中的a值压入操作数栈,最后计算赋值操作。完成压栈操作后,此时局部变量表中a=1,操作数栈中a=1。
- 然后执行++操作,++针对局部变量表操作,此时局部变量表中a=2,操作数栈中a=1。
- 最后完成赋值操作,把操作数栈中的a赋值给局部变量表中的a,导致a最终结果为1。
局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。局部变量表的容量以变量槽(Variable Slot)为最小单位,Java虚拟机规范并没有定义一个槽所应该占用内存空间的大小,但是规定了一个槽应该可以存放一个32位以内的数据类型。
操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出栈(LIFO)。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到方法的Code属性的max_stacks数据项中。
操作数栈的每一个元素可以是任意Java数据类型,32位的数据类型占一个栈容量,64位的数据类型占2个栈容量,且在方法执行的任意时刻,操作数栈的深度都不会超过max_stacks中设置的最大值。
当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。
int b = a++;
- 执行到这一步时,局部变量a=1。将局部变量表中的a的值压入到操作数栈中a=1。
- 再对局部变量中的a执行++操作,局部变量表中的a=2。
- 再执行赋值操作,从操作数栈中取出a的值,并把值赋值给b,所以b=1。
int c = a+ ++a * a++;
- 先将局部变量表中的a的值压入操作数栈中,此时局部变量表a=2,操作数栈a=2。
- 由于运算符优先级不同,这里先执行 ++a 操作,即对局部变量表中的a进行++操作,再将局部变量表中的a压入操作数栈。此时,局部变量表中的a=3,操作数栈a=3。
- 再运算 a++,此时,先将局部变量表a的值压入操作数栈,再对局部变量表中的a进行++操作。最后,局部变量表中的a=4,操作数据栈中的a=3。
- 执行 * 操作,此时操作数栈中的数出栈,执行乘法操作,即2和3步中的a进行出栈。得到结果为9,此时可以把9看成一个临时变量,又被重新压回栈中。
- 然后执行加法操作,即1和4步骤得到的a=2和临时变量9出栈,并执行加法。
- 得到最终结果,并赋值给c。
总结:
- 赋值 = ,最后计算
- = 右边的从左到右加载值依次压入操作数栈
- 实际先算哪个,需要根据运算符的优先级来确定
- 自增、自减操作都是之间改变局部变量表中的变量值,不经过操作栈
- 最后的赋值之前,临时结果也是存储再操作数栈中的。