1. Variabile de referință
În limbajul Java, există două tipuri de variabile: variabile primitive și orice altceva. După cum se întâmplă, vom vorbi acum despre „toate celelalte”.
De fapt, ar fi mai corect să spunem că există variabile primitive și variabile de referință . Deci, care sunt aceste variabile de referință?
Spre deosebire de tipurile primitive, ale căror variabile stochează valori direct, variabilele de referință stochează referințe la obiecte. Adică există un obiect undeva în memorie, iar variabila de referință stochează pur și simplu adresa acestui obiect în memorie (o referință la obiect).
Numai tipurile primitive stochează valori direct în variabile. Toate celelalte tipuri stochează doar o referință de obiect . Apropo, ați întâlnit deja două astfel de tipuri de variabile - String
variabile și variabile matrice .
Atât o matrice, cât și un șir sunt obiecte stocate undeva în memorie. String
variabilele și variabilele matrice stochează doar referințe la obiecte.
int a, int b and double d
sunt variabile primitive care își stochează valorile în interiorul lor.
O String str
variabilă este o referință și stochează adresa (referința) unui String
obiect în memorie.
Când se atribuie o valoare primitivă unei variabile de tip primitiv, valoarea acesteia este copiată (duplicată). Când se atribuie o variabilă de referință, este copiată doar adresa obiectului - obiectul în sine nu este copiat .
2. Despre ce sunt referințele?
Care este diferența fundamentală dintre variabilele de referință și variabilele primitive?
O variabilă primitivă este ca o cutie: puteți stoca o anumită valoare în ea. O variabilă de referință este mai mult ca o bucată de hârtie cu un număr de telefon pe ea.
O mașină vs cheile mașinii
Imaginează-ți că decizi să-i oferi prietenului tău o mașină de ziua lui. Nu o vei împacheta într-o cutie și nu o vei purta cu tine: mașina este prea mare pentru asta.
Este mult mai convenabil să prezinți doar cheile mașinii într-o cutie suficient de mare pentru a le conține. Prietenul tău va înțelege totul când va scoate cheile din cutie. Nu este nevoie să cărați întreaga mașină cu dvs. când puteți pur și simplu să predați cheile.
O persoană față de numărul ei de telefon
Sau iată o altă comparație: o persoană și numărul ei de telefon. Un număr de telefon nu este persoana, dar un număr de telefon poate fi folosit pentru a o suna, pentru a-i cere informații sau pentru a oferi instrucțiuni.
În mod similar, o referință este folosită pentru a interacționa cu un obiect. Toate obiectele interacționează între ele folosind referințe. În loc să „facem schimb de oameni”, pur și simplu schimbăm numere de telefon.
Când se atribuie o valoare unei variabile primitive, valoarea acesteia este copiată (duplicată). Când atribuiți o valoare unei variabile de referință, este copiată doar adresa (numărul de telefon) al obiectului - obiectul în sine nu este copiat.
O referință oferă încă un avantaj: puteți trece o referință de obiect unei anumite metode, iar metoda va putea modifica (schimba) obiectul folosind referința la acesta, apelând metodele sale și accesând datele din interiorul obiectului.
3. Atribuirea referințelor
La atribuirea variabilelor de referință, este atribuită doar adresa obiectului din memorie. Obiectele în sine nu apar sau dispar.
Această abordare evită copierea unor cantități mari de memorie. Dacă trebuie să treceți un obiect foarte mare unei metode, trecem doar referința la obiect și gata. Referința ocupă mult mai puțin spațiu.
Dimensiunea tuturor variabilelor de referință (indiferent de tipul lor) este aceeași - 4 octeți (ca un int). Dar! Dacă aplicația dvs. rulează pe o mașină Java pe 64 de biți, atunci toate referințele vor avea o dimensiune de 8 octeți (64 de biți).
În plus, referințele pot fi alocate doar unul altuia. Nu puteți modifica referințe sau aloca valori arbitrare variabilelor de referință:
Cod | Descriere |
---|---|
|
Acest lucru este permis |
|
Dar acest lucru nu este permis |
|
Și acest lucru nu este permis |
4. O null
referință
Și ce stochează o variabilă de referință dacă nu i s-a atribuit încă nimic?
Stochează o referință nulă . null
este un cuvânt cheie Java special care înseamnă absența unei referințe (o referință goală). Valoarea null
poate fi atribuită oricărei variabile de referință.
Toate variabilele de referință sunt, null
cu excepția cazului în care li s-a atribuit un fel de referință.
Exemple:
Cod | Descriere |
---|---|
|
Variabila String name are o valoare implicită: null . Variabila int age are o valoare implicită: 0 . |
Variabilele locale cărora nu li s-a atribuit o valoare sunt considerate neinițializate atât pentru tipurile primitive, cât și pentru cele de referință.
Dacă o variabilă stochează o referință la un obiect și doriți să ștergeți valoarea variabilei, atunci atribuiți-i o referință nulă.
Cod | Descriere |
---|---|
|
s magazine null . s stochează o referință la un șir de obiecte s stochează null . |
5. Transmiterea referințelor la metode
Dacă o metodă are parametri care sunt tipuri de referință , atunci valorile sunt transmise metodei în același mod ca atunci când se lucrează cu variabile fără referință. Parametrului i se atribuie pur și simplu valoarea celeilalte variabile.
Exemplu:
Cod | Descriere |
---|---|
|
Umple fill matricea transmisă ( array ) cu valoarea transmisă ( value ). |
Când fill
metoda este apelată, array
parametrului i se atribuie o referință matricei data
. Variabilei value
i se atribuie o referință obiectului șir ("Bună ziua").
Iată cum arată memoria înainte de a apela fill
metoda:
Iată cum arată memoria când fill
metoda rulează :

Variabilele data
și array
se referă la (stochează referințe la) același container în memorie.
Variabila value
stochează o referință la obiectul șir ( "Hello"
).
De asemenea, celulele matricei stochează doar referințe la "Hello"
obiect.
De fapt, niciun obiect nu este duplicat - sunt copiate doar referințele.
6. Comparație cu limbajul C/C++
În interviuri, uneori programatorii Java sunt întrebați cum sunt transmise datele către metodele din Java? Și uneori întrebarea este dacă datele sunt transmise prin referință sau prin valoare?
Această întrebare vine din C++, dar nu este foarte semnificativă în Java . În Java, parametrilor li se atribuie întotdeauna pur și simplu valorile argumentelor. Deci răspunsul corect ar fi „ după valoare ”.
Dar fiți pregătit să vă explicați poziția , deoarece este posibil să auziți imediat replica: „tipurile primitive sunt transmise după valoare, iar tipurile de referință sunt transmise prin referință”.
Această origine a acestei probleme provine din faptul că mulți programatori Java au fost programatori C++ în trecut. În acel limbaj de programare, întrebarea cu privire la modul în care parametrii sunt transferați metodelor era foarte importantă.
În Java, totul nu este ambiguu: tipurile primitive stochează valori, iar tipurile de referință stochează și o valoare - o referință. Este o întrebare dacă o variabilă este considerată o valoare .
În C++, o variabilă poate stoca atât o referință la un obiect, cât și la obiectul însuși. Același lucru a fost valabil și pentru tipurile primitive: o variabilă primitivă ar putea stoca o valoare sau declara variabila ca referință la un int
. Așadar, pentru a evita confuzia, programatorii C++ se referă întotdeauna la obiect la o referință ca referință , iar obiectul în sine - ca o valoare.
În C++, puteți avea cu ușurință situația în care o variabilă conține un obiect, dar cealaltă conține o referință la acel obiect. În consecință, întrebarea ce stochează o variabilă - obiectul în sine sau doar o referință la acesta - a fost foarte importantă. Când un obiect a fost transmis unei metode, acesta a fost copiat (dacă a fost transmis prin valoare) și nu copiat (dacă a fost transmis prin referință).
În Java, această dualitate nu există, deci răspunsul corect este: argumentele sunt transmise metodelor Java prin valoare . Doar că atunci când vorbim de variabile de referință, această valoare este o referință.
GO TO FULL VERSION