Am urmatorul cod:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
Noi știm că el ar fi scris doar pentru x++ " sau "x=x+1, dar
x = x++` ar trebui să primul atribut" x " de la sine, iar mai târziu a incrementa. De ce " x "continua cu" 0 " ca valoare?
--update
Aici's bytecode:
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
}
Am'll citit despre instrucțiuni pentru a încerca să înțeleagă...
Notă: Inițial am postat C# cod în acest răspuns pentru scopuri de ilustrare, de C# vă permite să trece int
parametrii de referință cu ref
cuvinte cheie. Am'am decis să-l actualizeze cu real juridice cod Java folosind prima MutableInt
clasă am găsit pe Google la fel de aproximativă ce ref
în C#. Pot't spune cu adevărat dacă te ajută sau te doare răspunsul. Eu va spun ca eu personal am't face tot atât de mult de dezvoltare Java; deci, pentru toți știu că ar putea fi mult mai idiomatice modalități de a ilustra acest punct.
Poate dacă am scrie o metoda de a face echivalentul a ceea ce x++
nu se va face acest lucru mai clar.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
Nu? Incrementa valoarea a trecut și a reveni la valoarea inițială: care's definiția postincrement operator.
Acum, las's vedem cum acest comportament joacă în exemplul de cod:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
ce face? Trepte x
, da. Și apoi se întoarce de ce " x " fost înainte de incrementare. Această întoarcere valoare, atunci se atribuie la "x".
Deci, ordinea de valorile atribuite " x " este 0, apoi 1, apoi 0.
Acest lucru ar putea fi mai clar încă dacă ne re-scrie mai sus:
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.
Fixare pe faptul că atunci când înlocuiți " x "de pe partea stângă de mai sus misiune cu "y", "puteți vedea că primele trepte x, și mai târziu atribuie-l la y" mi se pare confuz. Nu este "x", care este atribuit "y"; este valoarea anterior atribuite "x". Într-adevăr, injectarea " y " face lucrurile diferit de scenariul de mai sus; ne'am ajuns pur și simplu:
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.
Deci's clar: x = x++
efectiv nu se schimba valoarea lui x. Este întotdeauna provoacă x să aibă valori x0, atunci x0 + 1, și apoi x0 din nou.
**** Update: de Altfel, ca nu cumva să te îndoiești că " x " va ajunge vreodată atribuit 1 "intre" sporul de exploatare și atribuirea în exemplul de mai sus, am'am aruncat împreună un demo rapid pentru a ilustra faptul că această valoare intermediară într-adevăr, nu "exista" totuși niciodată nu va fi "vazut" pe fir de executare.
Demo apeluri x = x++;
într-o buclă în timp ce un thread separat continuu imprimă valoarea " x " pentru a consola.
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);
}
}
}
Mai jos este un fragment din programul de mai sus's de ieșire. Observa neregulate apariția atât 1s și 0s.
Incepand de fundal fir... 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1
x = x++
funcționează în felul următor:
x++
. Evaluarea acestei expresii produce o expresie de valoare (care este valoarea " x "înainte de creștere) și trepte "x".Deci, secvența de evenimente arată cum urmează (it's un real decompilat bytecode, ca produs de javap -c
, cu comentariile mele):
8: iload_1 // Amintiți-vă curentă valoarea x în stivă 9: iinc 1, 1 // Increment x (nu't modifica stiva) 12: istore_1 // Scrie amintiți valoare din stivă a x
Pentru comparație, x = ++x
:
8: iinc 1, 1 // Increment x 11: iload_1 // Push valoare de x pe stiva 12: istore_1 // Pop valoare din stivă a x
Acest lucru se întâmplă deoarece valoarea lui " x " nu't obține incrementat la toate.
x = x++;
este echivalent cu
int temp = x;
x++;
x = temp;
Explicație:
Las's se uite la byte-codul pentru această operațiune. Ia în considerare un eșantion de clasă:
class test {
public static void main(String[] args) {
int i=0;
i=i++;
}
}
Acum rulează clasa disassembler pe acest obținem:
$ 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
}
Acum Java VM este stack-based, ceea ce înseamnă că pentru fiecare operație, datele vor fi împins pe stivă și din stivă, datele vor apărea pentru a efectua operațiunea. Există, de asemenea, o altă structură de date, de obicei o matrice pentru a stoca variabilele locale. Variabilele locale sunt date de id-uri care sunt doar indicii pentru matrice.
Să ne uităm la mnemonice in `main () metoda:
iconst_0
: constantă Cu valoarea "0"
este împins pe stivă.istore_1
: elementul De sus din
stiva este apărut și stocate în
variabile locale cu indicele " 1 " iload_1
: valoarea la
locație 1
care este valoarea "x"
care este 0
, este împins în stivă.iinc 1, 1
: valoarea la
locație de memorie " 1 "este incrementat cu "1". Deci " x " devine acum
1
.istore_1
: valoarea la partea de sus a
stiva este stocat în locația de memorie1
. Care este 0
este atribuit
pentru " x " suprascrierea își crește valoarea.Prin urmare, valoarea " x " nu se modifică rezultă în buclă infinită.
Cu toate acestea "=
" are o mai mică prioritate operatorului decât "++
".
Deci x=x++;
ar trebui să evalueze, după cum urmează
Nici unul dintre răspunsuri în cazul în care destul de la fața locului, asa ca aici merge:
Când te're scris int x = x++
, ai're nu atribuirea " x "pentru a fi în sine la noua valoare,'re atribuirea de" x " pentru a fi valoarea de returnare a x++
expresie. Care se întâmplă să fie de la valoarea inițială de "x", după cum a sugerat în Colin Cochrane's a răspunde .
Pentru distracție, testați următorul cod:
public class Autoincrement {
public static void main(String[] args) {
int x = 0;
System.out.println(x++);
System.out.println(x);
}
}
Rezultatul va fi
0
1
Valoarea returnată a expresiei este valoarea inițială de "x", care este zero. Dar mai târziu, atunci când citesc în valoare de "x", vom primi valoarea actualizată , care este unul.
Acesta a fost deja explicat bine prin celelalte. Am includ link-uri relevante Java specification secțiuni.
x = x++ este o expresie. Java va urma evaluare order. Prima dată se va evalua expresia x++, care va incrementa x și a stabilit rezultatul valoare la valoarea anterioară a x. Apoi se va atribui expresia rezultatul]3 pentru variabila x. La final, x este înapoi la valoarea sa anterioară.
Această declarație:
x = x++;
evaluează astfel:
Deci valoarea este neschimbată. Compara cu:
x = ++x;
care evaluează ca:
Ceea ce vrei tu este:
while (x < 3) {
x++;
System.out.println(x);
}
Răspunsul este destul de simplu. Ea are de a face cu ordinea lucrurile sunt evaluate. x++
întoarce valoarea " x "atunci trepte "x".
În consecință, valoarea expresiei x++este
0. Deci tu esti atribuirea x=0
de fiecare dată în buclă. Cu siguranță x++
trepte de această valoare, dar asta se întâmplă înainte de cesiune.
De http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
incrementare/decrementare operatorii pot fie aplicate înainte (prefix) sau după (postfix) operandul. Codul rezultatul++; ++și rezultat; va ajunge în rezultatul fiind incrementat cu unu. singura diferență este că prefixul versiune (++rezultat) evaluează la crește valoarea, întrucât postfix versiune (rezultat++) evaluează la valoarea originală. Dacă sunteți doar efectuarea unui simplu incrementare/decrementare, nu't într-adevăr conteaza ce versiune ai alege. Dar dacă utilizați acest operator în parte de un expresie mai mare, cea pe care ai alege poate face o semnificativă diferența.
Pentru a ilustra, încercați următoarele:
int x = 0;
int y = 0;
y = x++;
System.out.println(x);
System.out.println(y);
Care va imprima 1 și 0.
Te're eficient obtinerea următorul comportament.
Ideea fiind că post-increment operator (x++) incrementează variabila în cauză DUPĂ întoarcerea valoarea sa de utilizare în ecuația it's utilizate în.
Edit: Adăugarea un pic ușor pentru a comenta. Ia în considerare, cum ar fi următoarele.
x = 1; // x == 1
x = x++ * 5;
// First, the right hand side of the equation is evaluated.
==> x = 1 * 5;
// x == 2 at this point, as it "gave" the equation its value of 1
// and then gets incremented by 1 to 2.
==> x = 5;
// And then that RightHandSide value is assigned to
// the LeftHandSide variable, leaving x with the value of 5.
Nu't într-adevăr nevoie de cod mașină pentru a înțelege ceea ce's happending.
Conform definițiilor:
1.1. Valoarea curenta a lui x este copiat în această variabilă temporară
1.2. x este incrementat acum.
Este destul de simplu.
Valoarea rămâne la 0 pentru valoarea x++
este 0. În acest caz, nu't contează dacă valoarea x
este crescut sau nu, atribuirea x=0` este executat. Acest lucru va suprascrie temporar incrementat valoarea " x " (care a fost de 1 pentru o "timp foarte scurt").
Cred că de x++ ca un apel de funcție care "întoarce" ce X a fost înainte de creștere (ca's de ce-l's a numit-o post-increment).
Astfel încât operațiunea comanda este: 1: cache valoarea lui x înainte de incrementarea 2: incrementa x 3: întoarcerea în cache valoare (x înainte de aceasta a fost incrementat) 4: valoarea de retur este atribuit x
x++
=: (x = x + 1) - 1
Deci:
x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!
Întrucât
++x
=: x = x + 1
Deci:
x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x
Desigur, rezultatul final este același ca pentru x++; " sau " ++x;` pe o linie de la sine.
Când ++ este pe rhs, rezultatul este returnat înainte numărul este incrementat. Schimbare a ++x și ar fi fost bine. Java ar fi optimizată acest lucru pentru a efectua o singură operațiune (atribuirea x-x), mai degrabă decât de creștere.
Precum și măsura în care pot vedea, eroarea apare, ca urmare a cesiunii imperative incrementat valoarea, cu valoarea înainte de incrementare, respectiv se anulează sporul.
În mod special, la "x++" expresia are valoarea de 'x' înainte de a incrementa spre deosebire de "++x", care are valoarea de 'x' după incrementare.
Dacă sunteți interesat în investigarea bytecode, vom lua o privire la cele trei linii în cauză:
7: iload_1
8: iinc 1, 1
11: istore_1
7: iload_1 # Va pune valoarea 2 variabile locale pe stiva
8: iinc 1,1 # va incrementa 2 variabile locale cu 1, rețineți că lasă stiva neatins!
9: istore_1 # Va apărea în partea de sus a stivei și de a salva valoarea acestui element a 2 variabile locale
(Puteți citi efecte de fiecare JVM instruire aici)
Acesta este motivul pentru codul de mai sus va bucla pe termen nelimitat, în timp ce versiunea cu ++x nu va fi. În bytecode pentru ++x ar trebui să arate destul de diferit, din câte îmi amintesc de la 1.3 Java compiler am scris un pic peste un an în urmă, bytecode-ar merge ceva de genul asta:
iinc 1,1
iload_1
istore_1
Deci schimbarea a două linii în primul rând, schimbă semantica, astfel încât valoarea lăsat pe partea de sus a stivei, după creștere (de exemplu, la 'valoare' de exprimare) este valoarea după creștere.
Verificați codul de mai jos,
int x=0;
int temp=x++;
System.out.println("temp = "+temp);
x = temp;
System.out.println("x = "+x);
producția va fi,
temp = 0
x = 0
post incrementareînseamnă **incrementa valoarea și returnează o valoare de incrementare**. De aceea, valoarea "temp" este
0. Și ce dacă
temp = i` și acest lucru este într-o buclă (cu excepția pentru prima linie de cod). doar ca in intrebare!!!!!!