今天在班里学了自增操作,先看一个简单的案例:
public class Test{
public static void main(String[] args){
int a=10;
int m=7+a++;// m=7+a a=a+1
System.out.println(a);//11
System.out.println(m);//17
}
}
i++或者++i参与到运算中的时候,有个特定的规律:
i++ : 先运算,后加1
++i : 先加1,后运算
但是严格来说两个都是先自增的(先记住这句话)
所以上面的题,int m=7+a++;
计算过程:(1)先计算 m=7+a (2)再计算: a=a+1
所以最后结果 a=11 m=17
上面的就是个常规题,你要是上面的都没看懂 ,那么下面的也不用看了。。。。
今天上课有个学生问我 :i=i++ 为啥不遵照上面的规律 ???
先看一个代码:
public class Test{
public static void main(String[] args){
int a=10;
int varNum=66;
varNum=varNum++;
System.out.println(varNum);
}
}
按照上面的道理,varNum经过 varNum=varNum++操作后 ,应该变为 67,
但是实际结果,发现varNum的值 还是66:
这是为什么?
底层原理
(1)栈
栈最上面是栈顶,下面是栈底
局部变量的值,在内存分析的时候,都被放入了栈中,栈的特点是先进后出,意味着先放进去的数,会被放在下面,后进去的数,一个一个垒在上面(就像往筒中放乒乓球)
(2)虚拟机指令—假如看不懂,可以先略过这部分往下看,然后回头再回来看~~~
要铺垫一些简单的虚拟机指令:(以下截图都是我从百度找的)
第一个:bipush
第二个:istore_1
第三个:iload_1
第四个:iinc(increment代表自增,这是一个运算指令)
上面的两个技能点,是两个铺垫技能点,下面开始讲解 varNum=varNum++运算:
首先先介绍一个JDK自带的反编译工具: 一个命令 javap 可以看到到底底层是怎么执行上面的代码的!
首先先编译Demo.java文件:
此时已经在我电脑的D盘train2018文件夹下 生成了Demo.class文件!
然后执行javap命令:
上面图中,红色部分,就是底层 varNum=varNum++的执行过程:
那么通过下面的解释,结合上面铺垫的虚拟机指令,来讲解红色框部分:
0: bipush 10 将参数10压入操作数栈;
2: istore_1 操作数栈中弹出一个数,赋给第一个局部变量:a
3: bipush 66 将参数66压入操作数栈;
5: istore_2操作数 栈中弹出一个数,赋给第二个局部变量:varNum
6: iload_2 将第二个局部变量varNum的值入操作数栈,此时操作数栈顶的值为66
7: iinc 2, 1 指令iinc对给定的局部变量做自增操作(注意这条指令没有改变操作数栈中的数据)
2, 1 表示对第2个局部变量varNum进行自增1操作 ,意味着 varNum 变为了 67(这是不是先自增)
10: istore_2 栈顶弹出一个数:也就是66 赋给第二个局部变量 varNum(这是不是赋值,只是赋的值为自增前的)
意味这 varNum的值 又变回66了。
最终打印结果 就是: 66
通俗理解
当时我的理解是:
varNum=varNum++
等价于
varNum=varNum;
varNum++;
(其实C语言中是这样的)
如果是这样的话那么最终varNum应该是67。将代码写成那种形式之后结果确实是67.
因此证明我的猜测是错误的。
再回到之前的让你记住的那句话:但是严格来说两个都是先自增的
,也经过了上面的分析确实是这么回事,因此不难得出以上代码等价于:
temp =varNum;
varNum++;
varNum=temp;