题目: j初始值为0,j=j++;循环100遍值为多少?
int j = 0;
for (int i = 0; i < 100; i++)
{
j = j++;
}
System.out.println(j);
答案: 0
原理探析:
1、当然如果我们讨论的深入一些,这个问题甚至可以从多线程原子性和易变性(Atomicity and Volatility)谈起。原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。
对域中的值做赋值和返回操作通常是原子性的,但是在C++中甚至下面的操作都可能是原子性的:
i++; // might be atomic in C++i += 2; // might be atomic in C++
当然在C++中,这取决于编译器跟处理器。(摘自《Java编程思想》一书)
2、这里我们不打算从线程安全的角度去讨论这个问题,通过JDK自带的反编译工具Javap,可以帮助我们一窥Java变异器在这个问题上的处理过程,从而明白问题的原理。
① Javap
② 通过两段代码及其反编译后的代码的比较分析题目
Java源代码:
package cn.lilin.test; public class JPlusPlus { public static void main(String[] args) { int j = 0; for (int i = 0;i < 100; i++) { j = j++; } System.out.println(j); } } class JPlusPlus2 { public static void main(String[] args) { int j = 0; for (int i = 0; i < 100; i++) { j = ++j; } System.out.println(j); } }
反编译后:
D:\JAVA_PROGRAM>javap -c cn.lilin.test.JPlusPlus
Compiled from "jplusplus.java"
public class cn.lilin.test.JPlusPlus extends java.lang.Object{
public cn.lilin.test.JPlusPlus();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0 //把0放到栈顶
1: istore_1 //把栈顶的值放到局部变量1中,即j中,j=0
2: iconst_0 //把0放到栈顶
3: istore_2 //把栈顶的值放到局部变量2中,即i中,i=0
4: iload_2 //把2(即i)放到栈顶
5: bipush 100
7: if_icmpge 21
10: iload_1 //把1放到栈顶(即把j的值放到栈顶)
11: iinc 1, 1 //把局部变量1(即j)增加1,此时栈顶仍然是0,而j=1
14: istore_1 //将栈顶的值(0)赋给1(即变量j),此时j又等于0了
15: iinc 2, 1 //把局部变量2(即i)增加1,此时i=1
18: goto 4 //循环
21: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_1
25: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
28: return
}
D:\JAVA_PROGRAM>javap -c cn.lilin.test.JPlusPlus2
Compiled from "jplusplus.java"
class cn.lilin.test.JPlusPlus2 extends java.lang.Object{
cn.lilin.test.JPlusPlus2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_0
3: istore_2
4: iload_2
5: bipush 100
7: if_icmpge 21
10: iinc 1, 1 //把局部变量1(即j)加1,此时栈顶为0,j=1
13: iload_1 //把j放到栈顶,此时栈顶为1,j仍是1
14: istore_1 //把栈顶的值赋给j,此时j还是1
15: iinc 2, 1 //把i加1
18: goto 4
21: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_1
25: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
28: return
}
源码中的区别:
一个是 j = j++;
一个是 j = ++j;
反编译代码的区别:
一个是 iload_1 iinc 1, 1
一个是 iinc 1, 1 iload_1
反汇编代码还有一个共同的语句:istore_1
我们再看一个例子:
public class TestJavap {
public static void main(String [] args) {
int i = 1;
i = i++;
int j = 1;
j = ++j;
}
}
反编译结果:
分析一下:
Code:
0: iconst_1 //把1放到栈顶
1: istore_1 //把栈顶的值放到局部变量1中(即i中)
2: iload_1 //把1(即i的值)放入栈顶
3: iinc 1, 1 //局部变量1(即i)加1变为2,此时i=2,注意这时栈中仍然是1,没有改变
6: istore_1 //把栈顶的值放到局部变量1中(即i中),即i这时候由2变成了1
7: iconst_1 //把1放到栈顶
8: istore_2 //把栈顶的值放到局部变量2中(即j中)
9: iinc 2, 1 //局部变量2(即j)加1变为2,此时j=2,注意这时栈中仍然是1,没有改变
12: iload_2 //把局部变量2(即j)的值放到栈顶,此时栈顶的值变为2
13: istore_2 //把栈顶的值放到局部变量2中,即j这时候真正由1变成了2
14: return
关于更多Javap的讨论这里有一篇写的比较清晰的博文Javap学习日记