深入理解Java中的自增
来源:互联网 发布:视频聊天软件哪个好 编辑:程序博客网 时间:2024/06/05 21:12
引言
i++和++i的区别在学习程序设计的时候应该就已经学过:一个是用完再加,一个是加完再用。那么考虑一下下面的代码:
int i = 0;for (int j = 0; j < 100; j++) i = i++;
这个运行完,i的值应该是多少呢?
深入理解自增
上面的代码运行完,i的值不是100,也不是101,而是0!这个结果的原因搜一下,会看到一个新的专业术语中间缓存变量,i = i++
这行代码相当于:
int tmp = i;i++;i = tmp;
单单知道这个也没用,你只是以后遇到i = i++
知道它是怎么处理的,但是换一个i = ++i + i++
,这个又是怎么运算的呢?
所以我们看一下经过javac编译后的int i = 0; i = i++
字节码到底是什么样子的,用javap -verbose
命令得到:
0: iconst_0 常数0入栈 1: istore_1 将栈顶元素移入本地变量1号位置存储 2: iload_1 本地变量1号位置入栈 3: iinc 1, 1 将本地变量1号位置加1 6: istore_1 栈顶元素移入本地变量1号位置 7: return 返回
对于JVM来说,最初的Java代码就是执行这7行字节码,每一行的意思我在后面都解释了,比较简单,主要是要理解,对于一个方法来说,都相当于在栈里压入了一个栈帧,在其中会有局部变量区和操作栈,前者就是存储局部变量的,后者是存操作数的(符合栈的规律,栈顶元素先出)
PC寄存器就是记录当前运行到哪一行指令了。
再比较一下i = ++i
的字节码:
0: iconst_0 1: istore_1 2: iinc 1, 1 5: iload_1 6: istore_1 7: return
字节码对应的操作其实都一样,只不过是顺序不一样。
i++是将局部变量区的值先load到栈中,再对局部变量区的值进行增加,而++i则是先对局部变量区的值进行增加,之后再load到栈中。这也很好的解释了为什么一个是用完再加,一个是加完再用。
再看看i = ++i + i++
的字节码:
0: iconst_0 1: istore_1 2: iinc 1, 1 5: iload_1 6: iload_1 7: iinc 1, 110: iadd 取栈顶的两个元素相加,再把结果压入栈11: istore_112: return
是不是也是一样的,只是iinc与iload的顺序不一样而已。所以以后再遇到这种情况,只要自己画一个操作栈,一个局部变量区,结果就显而易见了。
static的自增
static int i; public static void main(String[] str) { i = ++i + i++; }
对于这一段代码,它的字节码是:
0: getstatic #2 // Field i:I 3: iconst_1 4: iadd 5: dup 取当前栈顶元素复制一份,并压入栈中 6: putstatic #2 // Field i:I 9: getstatic #2 // Field i:I 12: dup 13: iconst_1 14: iadd 15: putstatic #2 // Field i:I 18: iadd 19: putstatic #2 // Field i:I 22: return
没有用到iinc,用了dup和iadd替代了。getstatic和putstatic分别是获取指定类的实例域,并压入栈以及为指定的类的静态域赋值。
对于++i,对应的字节码是先iadd,再dup,然后putstatic,这样dup的值就是加后值,而对于i++,对应的字节码是先dup,再iadd,再putstatic,这样dup的值就是原始的值。
虽然类静态变量和局部变量的字节码对应的操作不一样,但是结果都是一样的。
练习
public static void main(String[] str) { int i = 0; i = i++ + ++i; int j = 0; j = ++j + j++ + j++ + j++; int k = 0; k = k++ + k++ + k++ + ++k; int h = 0; h = ++h + ++h; int p1 = 0, p2 = 0; int q1 = 0, q2 = 0; q1 = ++p1; q2 = p2++; StringBuilder builder = new StringBuilder(); builder.append("i:").append(i).append("\n") .append("j:").append(j).append("\n") .append("k:").append(k).append("\n") .append("h:").append(h).append("\n") .append("p1:").append(p1).append("\n") .append("p2:").append(p2).append("\n") .append("q1:").append(q1).append("\n") .append("q2:").append(q2); System.out.println(builder.toString()); }
代码结果是:
i:2j:7k:7h:3p1:1p2:1q1:1q2:0
如果答案都是正确的那么就差不多可以过关了,如果有什么问题再自己去javap去研究研究字节码吧。
- 深入理解Java中的自增
- 深入理解Java的自增运算
- 深入理解java中的clone
- 深入理解Java中的finally
- 深入理解java中的clone
- 深入理解Java中的多线程
- 深入理解java中的clone
- 深入理解java中的clone
- 深入理解java'中的String
- Java中的ThreadLocal深入理解
- 深入理解Java中的IO
- 深入理解Java中的Map
- 深入理解Java中的容器
- 深入理解Java中的流
- 深入理解Java中的线程
- 深入理解Java中的String
- 深入理解Java中的String
- 深入理解Java中的String
- 关于树的总结
- 换源,安装ssh,lamp
- myeclipse在运行时tomcat 5.5.23启动不了
- iOS图片实现可拉伸不变形的处理操作
- rmq 求区间内出现次数最多的数字出现的次数!
- 深入理解Java中的自增
- 二进制数中的1的个数
- themeskinning使用和改进
- 二叉树,完全二叉树,满二叉树,二叉排序树,平衡二叉树,红黑树,B数,B-树,B+树,B*树
- Tensorflow 图像处理函数
- 用 chart.js 创建漂亮图表 (HTML绘制工具库)
- sleep和wait有什么区别
- 编辑器使用指南
- c++智能指针(SmartPtr)简单剖析