深入理解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去研究研究字节码吧。