1. Variabili di riferimento

Nel linguaggio Java ci sono due tipi di variabili: variabili primitive e tutto il resto. Si dà il caso che ora parleremo di "tutto il resto".

Sarebbe infatti più corretto dire che esistono variabili primitive e variabili di riferimento . Quindi quali sono queste variabili di riferimento?

A differenza dei tipi primitivi, le cui variabili memorizzano direttamente i valori, le variabili di riferimento memorizzano i riferimenti agli oggetti. Cioè, c'è un oggetto da qualche parte in memoria e la variabile di riferimento memorizza semplicemente l'indirizzo di questo oggetto in memoria (un riferimento all'oggetto).

Solo i tipi primitivi memorizzano i valori direttamente all'interno delle variabili. Tutti gli altri tipi memorizzano solo un riferimento all'oggetto . A proposito, hai già incontrato due di questi tipi di variabili: Stringvariabili e variabili di matrice .

Sia un array che una stringa sono oggetti archiviati da qualche parte nella memoria. Stringle variabili e le variabili array memorizzano solo i riferimenti agli oggetti.

Variabili di riferimento in Java

int a, int b and double dsono variabili primitive che memorizzano i loro valori al loro interno.

Una String strvariabile è un riferimento e memorizza l'indirizzo (riferimento) di un Stringoggetto in memoria.

Quando si assegna un valore primitivo a una variabile di tipo primitivo, il suo valore viene copiato (duplicato). Quando si assegna una variabile di riferimento, viene copiato solo l'indirizzo dell'oggettol'oggetto stesso non viene copiato .


2. Cosa sono i riferimenti?

Qual è la differenza fondamentale tra variabili di riferimento e variabili primitive?

Una variabile primitiva è come una scatola: puoi memorizzare un valore in essa. Una variabile di riferimento è più simile a un pezzo di carta con sopra un numero di telefono.

Una macchina contro le chiavi della macchina

Immagina di decidere di regalare un'auto al tuo amico per il suo compleanno. Non lo avvolgerai in una scatola e lo porterai con te: l'auto è troppo grande per quello.

È molto più conveniente presentare solo le chiavi della macchina in una scatola abbastanza grande da contenerle. Il tuo amico capirà tutto quando tirerà fuori le chiavi dalla scatola. Non c'è bisogno di portare con sé l'intera vettura, basta consegnare le chiavi.

Una persona contro il suo numero di telefono

Oppure ecco un altro paragone: una persona e il suo numero di telefono. Un numero di telefono non è la persona, ma un numero di telefono può essere utilizzato per chiamarla, chiederle informazioni o fornire istruzioni.

Allo stesso modo, un riferimento viene utilizzato per interagire con un oggetto. Tutti gli oggetti interagiscono tra loro utilizzando i riferimenti. Invece di "scambiare persone", scambiamo semplicemente i numeri di telefono.

Quando si assegna un valore a una variabile primitiva, il suo valore viene copiato (duplicato). Quando si assegna un valore a una variabile di riferimento, viene copiato solo l'indirizzo (numero di telefono) dell'oggetto, l'oggetto stesso non viene copiato.

Un riferimento offre un ulteriore vantaggio: puoi passare un riferimento a un oggetto a un metodo e il metodo sarà in grado di modificare (cambiare) l'oggetto utilizzando il riferimento ad esso, chiamando i suoi metodi e accedendo ai dati all'interno dell'oggetto.


3. Assegnazione delle referenze

Quando si assegnano variabili di riferimento, viene assegnato solo l'indirizzo dell'oggetto in memoria. Gli oggetti stessi non appaiono né scompaiono.

Questo approccio evita di copiare grandi quantità di memoria. Se devi passare un oggetto molto grande a un metodo, passiamo semplicemente il riferimento all'oggetto e il gioco è fatto. Il riferimento occupa molto meno spazio.

Assegnazione delle referenze

La dimensione di tutte le variabili di riferimento (indipendentemente dal loro tipo) è la stessa: 4 byte (come un int). Ma! Se la tua applicazione è in esecuzione su una macchina Java a 64 bit, tutti i riferimenti avranno una dimensione di 8 byte (64 bit).

Inoltre, le referenze possono essere assegnate solo l'una all'altra. Non è possibile modificare i riferimenti o assegnare valori arbitrari alle variabili di riferimento:

Codice Descrizione
String hello = "Hello";
String s = hello;
Questo è permesso
String hello = "Hello";
hello++;
Ma questo non è permesso
String hello = 0x1234;
E questo non è permesso

4. Un nullriferimento

E cosa memorizza una variabile di riferimento se non le è stato ancora assegnato nulla?

Memorizza un riferimento nullo . nullè una parola chiave Java speciale che indica l'assenza di un riferimento (un riferimento vuoto). Il nullvalore può essere assegnato a qualsiasi variabile di riferimento.

Tutte le variabili di riferimento lo sono nulla meno che non abbiano avuto un qualche tipo di riferimento assegnato loro.

Esempi:

Codice Descrizione
class Person
{
   public static String name;
   public static int age;
}


La String namevariabile ha un valore predefinito: null.
La int agevariabile ha un valore predefinito: 0.

Le variabili locali a cui non è stato assegnato un valore sono considerate non inizializzate sia per i tipi primitivi che per i tipi di riferimento.

Se una variabile memorizza un riferimento a un oggetto e desideri cancellare il valore della variabile, assegnale semplicemente un riferimento nullo.

Codice Descrizione
String s = null;
s = "Hello";
s = null;
snegozi null.
smemorizza un riferimento a un oggetto stringa
smemorizza null.

5. Passaggio di riferimenti ai metodi

Se un metodo ha parametri che sono tipi di riferimento , i valori vengono passati al metodo nello stesso modo in cui si lavora con variabili non di riferimento. Al parametro viene semplicemente assegnato il valore dell'altra variabile.

Esempio:

Codice Descrizione
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");
   }
}


riempie filll'array passato ( array) con il valore passato ( value).

Quando fillviene chiamato il metodo, al arrayparametro viene assegnato un riferimento all'array data. Alla valuevariabile viene assegnato un riferimento all'oggetto stringa ("Ciao").

Ecco come appare la memoria prima di chiamare il fill metodo:

Passaggio di riferimenti ai metodi

Ecco come appare la memoria quando il fill metodo è in esecuzione :

Passaggio di riferimenti ai metodi 2

Le variabili datae arraysi riferiscono a (memorizzano riferimenti a) lo stesso contenitore in memoria.

La valuevariabile memorizza un riferimento all'oggetto stringa ( "Hello").

Le celle dell'array memorizzano solo i riferimenti all'oggetto "Hello".

In effetti, nessun oggetto viene duplicato, vengono copiati solo i riferimenti.



6. Confronto con il linguaggio C/C++

Nelle interviste, a volte ai programmatori Java viene chiesto come i dati vengono passati ai metodi in Java? E a volte la domanda è se i dati vengono passati per riferimento o per valore?

Questa domanda viene da C++, ma non è molto significativa in Java . In Java, ai parametri vengono sempre semplicemente assegnati i valori degli argomenti. Quindi la risposta corretta sarebbe " per valore ".

Ma preparati a spiegare la tua posizione , poiché potresti sentire immediatamente la replica: "i tipi primitivi vengono passati per valore e i tipi di riferimento vengono passati per riferimento".

Questa origine di questo problema deriva dal fatto che molti programmatori Java erano programmatori C++ in passato. In quel linguaggio di programmazione, la questione di come i parametri vengono passati ai metodi era molto importante.

In Java, tutto è inequivocabile: i tipi primitivi memorizzano valori e anche i tipi di riferimento memorizzano un valore, un riferimento. Si tratta di stabilire se una variabile è considerata un valore .

In C++, una variabile può memorizzare sia un riferimento a un oggetto sia l'oggetto stesso. Lo stesso valeva per i tipi primitivi: una variabile primitiva poteva memorizzare un valore o dichiarare la variabile come riferimento a un oggetto int. Quindi, per evitare confusione, i programmatori C++ fanno sempre riferimento all'oggetto a un riferimento come riferimento e all'oggetto stesso come valore.

In C++, potresti facilmente avere la situazione in cui una variabile contiene un oggetto, ma l'altra contiene un riferimento a quell'oggetto. Di conseguenza, la questione di cosa memorizza una variabile - l'oggetto stesso o solo un riferimento ad esso - era molto importante. Quando un oggetto veniva passato a un metodo, veniva copiato (se passato per valore) e non copiato (se passato per riferimento).

In Java, questa dualità non esiste, quindi la risposta corretta è: gli argomenti vengono passati ai metodi Java per valore . È solo che quando parliamo di variabili di riferimento, questo valore è un riferimento.