你真的懂i++和++i吗?
大部分老哥看这篇文章前可能都会认为
i++先赋值再加
++i加了再赋值
看了这篇文章后........(先卖个关子)
那接下来给老哥上一个题目
照老哥们那样理解这个题目j的值应该为10
按照 i++先赋值再加的思路来分析一遍,
第一次循环, j++的值为0,先赋值给j,那么j的值就为0,然后再进行j+1那么第一次循环后j的值就为1,
第二次循环j的值为2
......
第10次循环j的值就为 10
可事情并非向老哥想的那样发展,答案是j的值为0(脑瓜子是不是嗡嗡的)
可能你们会疑惑,难道我以前理解的概念是错的吗?
那我们先从运行时内存方向来分析
本地方法栈:为虚拟机使用到的Native方法服务
方法区:存储已经被虚拟机加载的类信息,常量,静态变量
堆内存:存储对象或者是数组,new出来的都存储在堆内存
栈内存(虚拟机栈):方法运行时使用的内存,比如main方法运行,进入方法栈中执行
程序计数器:记录每个线程执行的位置,用于线程恢复
我们只需要关注虚拟机栈就好了,每一个线程都有一个自己私有的虚拟机栈,每一个虚拟机栈里面有多个栈帧,每调用一个方法就会为每一个方法生成一个栈帧,每一个栈帧里面都有局部变量表,操作数栈,动态连接,方法返回地址
栈内存模型图
栈帧
局部变量表:局部变量值存储空间,用于存储方法参数和方法内部定义的局部变量
操作数栈:用来存放操作数,局部变量表中的变量是不可以直接使用,需要把局部变量表中的值复制过来再使用
动态连接
方法返回地址
再来看以下代码
这个c的值很明显为3,我们通过这个代码和jvm字节码来分析他们在栈帧中是如何运行的
Ps:在cmd窗口执行 javap -v 需要获取jvm字节码类名(不需要后缀名), 如: javap -v Test (获取Test类的字节码), 注意:不是java文件是class文件
Ps:具体的jvm字节码指令什么意思可以百度“jvm字节码指令表”
1. inconst_1 ( 将 int 型 1 推送至栈顶)
2. istore_1 (将栈顶 int 型数值存入第二个局部变量,为什么是第二个局部变量呢?因为第一个局部变量为main方法的args参数)
3.iconst_2 ( 将 int 型 2 推送至栈顶)
4. iconst_3 (将 int 型 3 推送至栈顶)
5.iload_1 ( 将第二个 int 型局部变量推送至栈顶)
6. iload_2 (将第三个 int 型局部变量推送至栈顶)
7.iadd( 将栈顶两 int 型数值相加并将结果压入栈顶)
8.istore_3 (将栈顶 int 型数值存入第四个局部变量)
9.getstatic( 获取指定类的静态字段,并将其压入栈顶)
10.iload_3 (将第四个 int 型局部变量推送至栈顶)
11.invokevirtual( 调用实例方法)
这个3步就是执行 “System.out.println(c)” 语句打印c的值
12.return (从当前方法返回 void)
按照这个方法我们再去分析上一个题,j的值为什么是0,先执行 javap -v Test 命令获取jvm字节码指令
注意看
10:iload_1 将第二个 int 型局部变量推送至栈顶(把j的值推送到栈顶)
11: iinc 1, 1 (M为非负整数,N为整数)将局部变量数组的 M个单元中的 int 值增加 N(也就是 j++,执行了这一条jvm指令码,此时j的值已经+1了,但是j++(0)此时还在操作数栈中,还没存入局部变量表,也就是说执行完这一行指令码,操作数栈中的值为0,而此时j的值已经为+1了)
14: istore_1 将栈顶 int 型数值存入第二个局部变量(这一行最关键,表示执行完这一行指令码的时候,他才吧操作数栈中的值,赋值给j,操作数栈中为0,然后赋值给j,所以j的值又变成0了)
看到这里老哥应该清楚,上一个题目的值j的值为什么为0了吧,
那么接下来给老哥们上一道题,猜猜答案为多少