我有以下代码:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
我们知道他应该只写x++或x=x+1,但在x=x+ +时,它应该先把x归为自己,然后再加1。为什么x的值是0 ?
- - -更新
下面是字节码:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
我会仔细阅读说明,试着理解……
这是因为x的值根本没有增加。
x = x++;
等于
int temp = x;
x++;
x = temp;
解释:
让我们看看这个操作的字节代码。考虑一个示例类:
class test {
public static void main(String[] args) {
int i=0;
i=i++;
}
}
现在运行类反汇编程序,我们得到:
$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
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: iload_1
3: iinc 1, 1
6: istore_1
7: return
}
现在Java虚拟机是基于堆栈的,这意味着对于每个操作,数据将被推入堆栈,从堆栈中,数据将弹出来执行操作。还有另一种数据结构,通常是存储局部变量的数组。局部变量的id是数组的索引。
让我们看看main()方法中的助记符:
iconst_0: The constant value 0
is pushed on to the stack.
istore_1: The top element of the
stack is popped out and stored in the
local variable with index 1 which
is x.
iload_1 : The value at the
location 1 that is the value of x
which is 0, is pushed into the stack.
iinc 1, 1 : The value at the
memory location 1 is incremented by 1. So x now becomes
1.
istore_1 : The value at the top of
the stack is stored to the memory location1. That is 0 is assigned
to x overwriting its incremented value.
因此,x的值不会改变,从而导致无限循环。
我想知道Java规范中是否有精确定义此行为的内容。(这句话的明显意思是我懒得去检查。)
注意Tom的字节码,关键行是7、8和11。第7行将x加载到计算堆栈中。第8行增加x。第11行将堆栈的值存储回x。在正常情况下,如果你不将值赋值回自身,我不认为有任何理由不能加载,存储,然后增加。你会得到同样的结果。
比如,假设你有一个更正常的情况,你写这样的东西:
z = (x + +) + (y + +);
它是否说(跳过技术细节的伪代码)
load x
increment x
add y
increment y
store x+y to z
or
load x
add y
store x+y to z
increment x
increment y
应该无关紧要。我认为,任何一种实现都是有效的。
我会非常谨慎地编写依赖于这种行为的代码。对我来说,它看起来非常依赖于实现。只有当您做了一些疯狂的事情(比如这里的例子),或者您有两个线程正在运行,并且依赖于表达式中的求值顺序时,才会产生影响。
X = x++的工作方式如下:
首先,它计算表达式x++。对该表达式求值产生一个表达式值(即x在递增前的值),并使x递增。
之后,它将表达式值赋给x,覆盖增量值。
因此,事件的序列如下所示(这是一个实际的反编译字节码,由javap -c生成,带有我的注释):
8: iload_1 // Remember current value of x in the stack
9: iinc 1, 1 // Increment x (doesn't change the stack)
12: istore_1 // Write remebered value from the stack to x
作为比较,x = ++x:
8: iinc 1, 1 // Increment x
11: iload_1 // Push value of x onto stack
12: istore_1 // Pop value from the stack to x