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 - Stringvariabile și variabile matrice .

Atât o matrice, cât și un șir sunt obiecte stocate undeva în memorie. Stringvariabilele și variabilele matrice stochează doar referințe la obiecte.

Variabile de referință în Java

int a, int b and double dsunt variabile primitive care își stochează valorile în interiorul lor.

O String strvariabilă este o referință și stochează adresa (referința) unui Stringobiect î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.

Atribuirea referințelor

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
String hello = "Hello";
String s = hello;
Acest lucru este permis
String hello = "Hello";
hello++;
Dar acest lucru nu este permis
String hello = 0x1234;
Și acest lucru nu este permis

4. O nullreferință

Și ce stochează o variabilă de referință dacă nu i s-a atribuit încă nimic?

Stochează o referință nulă . nulleste un cuvânt cheie Java special care înseamnă absența unei referințe (o referință goală). Valoarea nullpoate fi atribuită oricărei variabile de referință.

Toate variabilele de referință sunt, nullcu excepția cazului în care li s-a atribuit un fel de referință.

Exemple:

Cod Descriere
class Person
{
   public static String name;
   public static int age;
}


Variabila String nameare o valoare implicită: null.
Variabila int ageare 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
String s = null;
s = "Hello";
s = null;
smagazine null.
sstochează o referință la un șir de obiecte
sstochează 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
class Solution
{
   public static void fill(String[] array, String value)
   {
      for (int i = 0; i < array.length; i++)
        array[i] = value;
   }

   public static void main(String[] args)
   {
     String[] data = new String[10];
     fill(data, "Hello");
   }
}


Umple fillmatricea transmisă ( array) cu valoarea transmisă ( value).

Când fillmetoda este apelată, arrayparametrului i se atribuie o referință matricei data. Variabilei valuei se atribuie o referință obiectului șir ("Bună ziua").

Iată cum arată memoria înainte de a apela fill metoda:

Transmiterea referințelor la metode

Iată cum arată memoria când fill metoda rulează :

Transmiterea referințelor la metode 2

Variabilele datași arrayse referă la (stochează referințe la) același container în memorie.

Variabila valuestochează 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ță.