为什么这会进入无限循环?

我有以下代码:

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+1x = x++xx0

--更新

下面是字节码:

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

}

我将阅读有关说明以尝试理解...


答案 1

注意:最初,出于说明目的,我在此答案中发布了C#代码,因为C#允许您使用关键字通过引用传递参数。我决定使用我在Google上找到的第一个MutableInt类来更新它,以使用实际的合法Java代码来更新它,以近似于C#中的内容。我真的无法判断这是否有助于或伤害答案。我会说我个人没有做过那么多的Java开发。因此,据我所知,可以有更多惯用的方法来说明这一点。intrefref


也许如果我们写出一种方法来做相当于做什么,它会更清楚地说明这一点。x++

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

右?递增传递的值并返回原始值:这是增量后运算符的定义。

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

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

postIncrement(x)做什么?增量 ,是的。然后返回增量之前的 x。然后,此返回值将分配给 。xx

因此,分配给 的值的顺序是 0,然后是 1,然后是 0。x

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

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 的值。实际上,注入使事情与上面的场景没有什么不同;我们只是得到:xyxyy

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 具有值 x0,然后是 x0 + 1,然后再次是 x0x = x++


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

演示在循环中调用,而单独的线程连续将 的值输出到控制台。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

答案 2

x = x++按以下方式工作:

  • 首先,它计算表达式 。此表达式的计算将生成一个表达式值(即增量之前的值)并递增 。x++xx
  • 稍后,它将表达式值赋值给 ,覆盖递增的值。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