我有以下代码:

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准备分配(评估) x增加 x之前的值赋给x。

其他回答

    x++
=: (x = x + 1) - 1

So:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

   ++x
=: x = x + 1

So:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

当然最终结果和x++是一样的;或+ + x;在一条直线上。

这就像你期望的那样。这就是前缀和后缀的区别。

int x = 0; 
while (x < 3)    x = (++x);

据我所知,错误发生了,由于赋值覆盖了增量值,在增量之前的值,即它取消了增量。

具体来说,"x++"表达式在递增前的值为'x',而"++x"表达式在递增后的值为'x'。

如果你对研究字节码感兴趣,我们将看一下有问题的三行:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7: iload_1 #将第二个本地变量的值放在堆栈上 8: iinc 1,1 #将使第二个局部变量加1,注意它不影响堆栈! 9: istore_1 #将弹出堆栈顶部,并将该元素的值保存到第二个局部变量中 (你可以在这里阅读每条JVM指令的效果)

这就是为什么上面的代码将无限循环,而带有++x的版本不会。 ++x的字节码应该看起来很不一样,据我所知,在我一年多以前写的1.3 Java编译器中,字节码应该是这样的:

iinc 1,1
iload_1
istore_1

所以只是交换了前两行,改变了语义,使得留在堆栈顶部的值,在增量之后(即表达式的“值”)是增量之后的值。

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

Note: Originally I posted C# code in this answer for purposes of illustration, since C# allows you to pass int parameters by reference with the ref keyword. I've decided to update it with actual legal Java code using the first MutableInt class I found on Google to sort of approximate what ref does in C#. I can't really tell if that helps or hurts the answer. I will say that I personally haven't done all that much Java development; so for all I know there could be much more idiomatic ways to illustrate this point.


也许如果我们写一个方法来做x++所做的事情,就会更清楚。

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

对吧?对传递的值加1并返回原始值:这就是后加1操作符的定义。

现在,让我们看看这个行为是如何在你的示例代码中发挥作用的:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)做什么?增量x,对。然后返回x在增量之前的值。然后这个返回值被赋值给x。

所以x的值的顺序是0,然后是1,然后是0。

如果我们重写上面的代码,这可能会更清楚:

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

当你把上面赋值的左边的x替换为y时,“你可以看到它首先增加了x,然后把它归为y”,这让我很困惑。不是x被赋值给y;它是以前赋给x的值。实际上,注入y与上面的场景没有什么不同;我们得到:

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

很明显,x = x++不会改变x的值,它总是使x的值为x0,然后是x0 + 1,然后又是x0。


更新:顺便说一句,为了避免您怀疑x在增量操作和上面示例中的赋值之间被赋值为1,我已经组合了一个快速演示来说明这个中间值确实“存在”,尽管它永远不会在执行线程上“看到”。

演示调用x = x++;在循环中,另一个线程连续地将x的值打印到控制台。

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

下面是上述程序输出的摘录。注意1和0的不规则出现。

Starting background thread...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1