1.问题:遇到复杂运算,怎么办
如果只知道表面理解a++与++a的区别:a++,先使用,再赋值;++a,先自增,再使用。那么遇到复杂的运算就会迷糊了,比如
//第1题
int a = 1;
int c = a + ++a + a++;
System.out.printf("c="+c+",a="+a);//c等于5,a等于3
2.为什么输出结果是c=5,a=3呢?分析过程如下:
可能很多人都知道a=3,因为进行了2次自增,那c呢?其实答案都在字节码指令中,如下:
我们都知道jvm分为堆、方法区、虚拟机栈、本地方法栈,程序计数器,而是jvm在运行具体的方法时,会在虚拟机栈中创建对应的栈帧,栈帧中有局部变量表(存放基本类型变量,下面简称变量表)、操作数栈(用来存放计算的操作数以及返回结果,下面简称栈)、方法出口,动态链接。
那我们就开始分析字节码指令运行的过程:
0 iconst_1 :将常量1压入栈顶,栈中元素为1
1 istore_1 :将栈顶int类型的值,赋给变量表的第二个位置的变量,此时a = 1
2 iload_1 : 将变量表的第二个位置的变量的值,压入栈,即把a的值压入栈顶,栈元素为1
3 iinc 1 by 1:把变量表中的第二个位置的变量+1,与栈无关,即a已经等于2
6 iload_1 : 把变量表中的第二位置的变量压入栈1,即把2压入栈顶,此时栈元素为2,1
7 iadd :从栈顶弹出两个int类型的值,进行相加,再将结果压入栈,此时栈元素为3
8 iload_1 :将变量表中第二个位置的变量压入栈,即a=2压入栈,此时栈元素为2,3
9 iinc 1 by 1 :将变量表中第二个位置的变量+1,即a=3
12 iadd : 从栈顶弹出两个int类型的值,进行相加,再将结果压入栈,此时栈元素为5
13 istore_2 : 将栈顶int类型的值,赋给变量表的第三个位置的变量,即把5赋值给变量c
所以结果打印出来c=5,a=3。
3.总结
c= a + ++a + a++, 因为计算机是从左到右计算,从入栈角度,将等号右边式子分成三个主步骤(从+、-、*、/算术符号开始分割):
第一,将第一个a存入栈,此时a=1,栈元素为1
第二,因为++优先级高于+算术运算符,++a,即压栈前a先加1,此时a=2了,把2压入栈,此时栈元素为2、1,进行+,则栈元素为3
第三,a++,先将第三个a压入栈,此时a=2,栈元素为2,3,出栈计算2+3=5,并赋值给c,c=5,另外a自增,变成3。
简单技巧:无论多复杂的运算,如果是++a,后面的a就进行加1,如果是a++,就不变。上面例子中第二个a进行++a,即1+1 =2 ,所以第三个a已经变成了2,c=1+2+2=5,这种方法能简单又正确地计算