Примечание : Первоначально я написал код 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, а позже приписывает его 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 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.
Начало фоновой темы ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1