Ci sono già ottime risposte che riguardano questo. Volevo dare un piccolo contributo condividendo un esempio molto semplice (che verrà compilato) contrastando i comportamenti tra Pass-by-reference in c ++ e Pass-by-value in Java.
Alcuni punti:
- Il termine "riferimento" è sovraccarico di due significati separati. In Java significa semplicemente un puntatore, ma nel contesto di "Passa per riferimento" significa un handle per la variabile originale che è stata passata.
- Java è Pass-by-value . Java è un discendente di C (tra le altre lingue). Prima di C, diverse lingue precedenti (ma non tutte) come FORTRAN e COBOL supportavano PBR, ma C no. PBR ha permesso a queste altre lingue di apportare modifiche alle variabili passate all'interno delle routine secondarie. Al fine di realizzare la stessa cosa (cioè cambiare i valori delle variabili all'interno delle funzioni), i programmatori C hanno passato i puntatori alle variabili in funzioni. I linguaggi ispirati a C, come Java, hanno preso in prestito questa idea e continuano a passare il puntatore ai metodi come ha fatto C, tranne che Java chiama i suoi puntatori Riferimenti. Ancora una volta, questo è un uso diverso della parola "Riferimento" rispetto a "Passa per riferimento".
- C ++ consente il pass-by-reference dichiarando un parametro di riferimento usando il carattere "&" (che risulta essere lo stesso carattere usato per indicare "l'indirizzo di una variabile" in C e C ++). Ad esempio, se passiamo un puntatore per riferimento, il parametro e l'argomento non puntano solo allo stesso oggetto. Piuttosto, sono la stessa variabile. Se uno viene impostato su un indirizzo diverso o su null, anche l'altro.
- Nell'esempio C ++ sotto sto passando un puntatore a una stringa terminata null per riferimento . E nell'esempio Java seguente sto passando un riferimento Java a una stringa (di nuovo, lo stesso di un puntatore a una stringa) in base al valore. Notare l'output nei commenti.
C ++ passa per esempio di riferimento:
using namespace std;
#include <iostream>
void change (char *&str){ // the '&' makes this a reference parameter
str = NULL;
}
int main()
{
char *str = "not Null";
change(str);
cout<<"str is " << str; // ==>str is <null>
}
Java passa "un riferimento Java" per esempio di valore
public class ValueDemo{
public void change (String str){
str = null;
}
public static void main(String []args){
ValueDemo vd = new ValueDemo();
String str = "not null";
vd.change(str);
System.out.println("str is " + str); // ==> str is not null!!
// Note that if "str" was
// passed-by-reference, it
// WOULD BE NULL after the
// call to change().
}
}
MODIFICARE
Diverse persone hanno scritto commenti che sembrano indicare che o non stanno guardando i miei esempi o non ottengono l'esempio c ++. Non sono sicuro di dove sia la disconnessione, ma indovinare l'esempio c ++ non è chiaro. Sto pubblicando lo stesso esempio in pascal perché penso che il pass-by-reference sia più pulito in pascal, ma potrei sbagliarmi. Potrei solo confondere di più le persone; Spero di no.
In pascal, i parametri passati per riferimento sono chiamati "parametri var". Nella procedura setToNil di seguito, notare la parola chiave 'var' che precede il parametro 'ptr'. Quando un puntatore viene passato a questa procedura, verrà passato per riferimento . Nota il comportamento: quando questa procedura imposta ptr su zero (ovvero Pascal parla per NULL), imposterà l'argomento su zero - non puoi farlo in Java.
program passByRefDemo;
type
iptr = ^integer;
var
ptr: iptr;
procedure setToNil(var ptr : iptr);
begin
ptr := nil;
end;
begin
new(ptr);
ptr^ := 10;
setToNil(ptr);
if (ptr = nil) then
writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. }
end.
MODIFICA 2
Alcuni estratti da "THE Java Programming Language" di Ken Arnold, James Gosling (il ragazzo che ha inventato Java) e David Holmes, capitolo 2, sezione 2.6.5
Tutti i parametri ai metodi vengono passati "per valore" . In altre parole, i valori delle variabili dei parametri in un metodo sono copie dell'invocatore specificato come argomenti.
Continua a fare lo stesso punto per quanto riguarda gli oggetti. . .
Si noti che quando il parametro è un riferimento a un oggetto, è il riferimento a un oggetto, non l'oggetto stesso, che viene passato "per valore" .
E verso la fine della stessa sezione fa una dichiarazione più ampia sul fatto che java è solo un passaggio di valore e mai un riferimento.
Il linguaggio di programmazione Java non passa oggetti per riferimento; si
passa riferimenti agli oggetti per valore . Poiché due copie dello stesso riferimento si riferiscono allo stesso oggetto reale, le modifiche apportate attraverso una variabile di riferimento sono visibili attraverso l'altra. Esiste esattamente un parametro che passa la modalità: passa per valore e aiuta a mantenere le cose semplici.
Questa sezione del libro ha una grande spiegazione del passaggio di parametri in Java e della distinzione tra pass-by-reference e pass-by-value ed è del creatore di Java. Incoraggerei chiunque a leggerlo, soprattutto se non sei ancora convinto.
Penso che la differenza tra i due modelli sia molto sottile e, a meno che tu non abbia fatto la programmazione in cui hai effettivamente utilizzato il pass-by-reference, è facile perdere i due modelli.
Spero che questo risolva il dibattito, ma probabilmente non lo farà.
MODIFICA 3
Potrei essere un po 'ossessionato da questo post. Probabilmente perché ritengo che i produttori di Java abbiano inavvertitamente diffuso disinformazione. Se invece di usare la parola "riferimento" per i puntatori avessero usato qualcos'altro, diciamo Dingleberry, non ci sarebbero stati problemi. Si potrebbe dire "Java passa i mirtilli rossi per valore e non per riferimento", e nessuno sarebbe confuso.
Questo è il motivo per cui solo gli sviluppatori Java hanno problemi con questo. Guardano la parola "riferimento" e pensano di sapere esattamente cosa significhi, quindi non si preoccupano nemmeno di considerare l'argomento opposto.
Ad ogni modo, ho notato un commento in un post più vecchio, che ha creato un'analogia con i palloncini che mi è davvero piaciuta. Tanto che ho deciso di incollare insieme alcune clip art per realizzare una serie di cartoni animati per illustrare il punto.
Passare un riferimento per valore: le modifiche al riferimento non si riflettono nell'ambito del chiamante, ma lo sono le modifiche all'oggetto. Questo perché il riferimento viene copiato, ma sia l'originale che la copia fanno riferimento allo stesso oggetto.
Passa per riferimento : non esiste una copia del riferimento. Il riferimento singolo è condiviso sia dal chiamante che dalla funzione chiamata. Eventuali modifiche al riferimento o ai dati dell'oggetto si riflettono nell'ambito del chiamante.
MODIFICA 4
Ho visto post su questo argomento che descrivono l'implementazione a basso livello del passaggio dei parametri in Java, che ritengo sia eccezionale e molto utile perché rende concreta un'idea astratta. Tuttavia, per me la domanda è più sul comportamento descritto nelle specifiche del linguaggio che sull'implementazione tecnica del comportamento. Questo è un estratto dalle specifiche del linguaggio Java, sezione 8.4.1 :
Quando viene invocato il metodo o il costruttore (§15.12), i valori delle espressioni degli argomenti effettivi inizializzano le variabili dei parametri appena create, ciascuna del tipo dichiarato, prima dell'esecuzione del corpo del metodo o del costruttore. L'identificatore visualizzato in DeclaratorId può essere utilizzato come nome semplice nel corpo del metodo o del costruttore per fare riferimento al parametro formale.
Ciò significa che java crea una copia dei parametri passati prima di eseguire un metodo. Come la maggior parte delle persone che hanno studiato compilatori al college, ho usato "The Dragon Book", che è IL libro dei compilatori. Ha una buona descrizione di "Chiamata per valore" e "Chiamata per riferimento" nel Capitolo 1. La descrizione di Chiamata per valore corrisponde esattamente alle specifiche Java.
Quando studiavo compilatori, negli anni '90, ho usato la prima edizione del libro del 1986, che ha preceduto Java di circa 9 o 10 anni. Tuttavia, mi sono appena imbattuto in una copia della seconda edizione del 2007 che in realtà menziona Java! La sezione 1.6.6, intitolata "Meccanismi di passaggio dei parametri", descrive i passaggi dei parametri piuttosto bene. Ecco un estratto sotto l'intestazione "Call-by-value" che menziona Java:
In call-by-value, il parametro effettivo viene valutato (se è un'espressione) o copiato (se è una variabile). Il valore viene inserito nella posizione appartenente al corrispondente parametro formale della procedura chiamata. Questo metodo viene utilizzato in C e Java ed è un'opzione comune in C ++, così come nella maggior parte degli altri linguaggi.