Примечание : Первоначально я разместил код C # в этом ответе в целях иллюстрации, поскольку C # позволяет передавать параметры int
по ссылке с ключевым словом ref
.Я решил обновить его с помощью действующего легального Java-кода, используя первый класс MutableInt
, который я нашел в Google, чтобы приблизить то, что ref
делает в C #.Я не могу действительно сказать, помогает ли это или ранит ответ.Я скажу, что лично я не столько занимался разработкой Java;так что, насколько я знаю, может быть гораздо больше идиоматических способов проиллюстрировать этот момент.
Возможно, если мы напишем метод, который будет делать эквивалент того, что 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
, да.И тогда возвращает то, что 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, ипозже приписывает это "у меня поражает.Это не 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 принимать значения x 0 , затем x 0 + 1, а затем снова x 0 .
Обновление : Кстати, чтобы вы не сомневались, что 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