Es gibt bereits gute Antworten, die dies abdecken. Ich wollte einen kleinen Beitrag leisten, indem ich ein sehr einfaches Beispiel (das kompiliert wird) mit den Verhaltensweisen zwischen Pass-by-Reference in c ++ und Pass-by-Value in Java teile.
Ein paar Punkte:
- Der Begriff "Referenz" ist überladen mit zwei getrennten Bedeutungen. In Java bedeutet es einfach einen Zeiger, aber im Kontext von "Pass-by-Reference" bedeutet es ein Handle für die ursprüngliche Variable, die übergeben wurde.
- Java ist Pass-by-Value . Java ist ein Nachkomme von C (unter anderem). Vor C unterstützten mehrere (aber nicht alle) frühere Sprachen wie FORTRAN und COBOL PBR, C jedoch nicht. PBR erlaubte diesen anderen Sprachen, Änderungen an den übergebenen Variablen innerhalb von Unterroutinen vorzunehmen. Um dasselbe zu erreichen (dh die Werte von Variablen innerhalb von Funktionen zu ändern), haben C-Programmierer Zeiger auf Variablen in Funktionen übergeben. Von C inspirierte Sprachen wie Java haben diese Idee übernommen und übergeben weiterhin Zeiger auf Methoden wie C, außer dass Java seine Zeiger References aufruft. Auch dies ist eine andere Verwendung des Wortes "Referenz" als in "Pass-By-Reference".
- C ++ ermöglicht die Referenzübergabe durch Deklarieren eines Referenzparameters mit dem Zeichen "&" (das zufällig dasselbe Zeichen ist, mit dem sowohl in C als auch in C ++ "die Adresse einer Variablen" angegeben wird). Wenn wir beispielsweise einen Zeiger als Referenz übergeben, zeigen der Parameter und das Argument nicht nur auf dasselbe Objekt. Sie sind vielmehr dieselbe Variable. Wenn einer auf eine andere Adresse oder auf null gesetzt wird, tut dies auch der andere.
- Im folgenden C ++ - Beispiel übergebe ich einen Zeiger auf eine nullterminierte Zeichenfolge als Referenz . Und im folgenden Java-Beispiel übergebe ich einen Java-Verweis auf einen String (wieder dasselbe wie ein Zeiger auf einen String) nach Wert. Beachten Sie die Ausgabe in den Kommentaren.
C ++ Pass als Referenzbeispiel:
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 übergeben "eine Java-Referenz" als Wertbeispiel
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().
}
}
BEARBEITEN
Einige Leute haben Kommentare geschrieben, die darauf hinweisen, dass sie entweder meine Beispiele nicht betrachten oder das C ++ - Beispiel nicht erhalten. Ich bin mir nicht sicher, wo sich die Trennung befindet, aber das Erraten des c ++ - Beispiels ist nicht klar. Ich poste das gleiche Beispiel in Pascal, weil ich denke, dass Pass-by-Reference in Pascal sauberer aussieht, aber ich könnte mich irren. Ich könnte die Leute mehr verwirren; Ich hoffe nicht.
In Pascal werden als Referenz übergebene Parameter als "var-Parameter" bezeichnet. Beachten Sie in der folgenden Prozedur setToNil das Schlüsselwort 'var' vor dem Parameter 'ptr'. Wenn ein Zeiger an diese Prozedur übergeben wird, wird er als Referenz übergeben . Beachten Sie das Verhalten: Wenn diese Prozedur ptr auf nil setzt (das ist pascal speak for NULL), wird das Argument auf nil gesetzt - das können Sie in Java nicht tun.
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.
BEARBEITEN 2
Einige Auszüge aus "THE Java Programming Language" von Ken Arnold, James Gosling (dem Erfinder von Java) und David Holmes, Kapitel 2, Abschnitt 2.6.5
Alle Parameter an Methoden werden "nach Wert" übergeben . Mit anderen Worten, Werte von Parametervariablen in einer Methode sind Kopien des als Argumente angegebenen Aufrufers.
Er fährt fort, den gleichen Punkt in Bezug auf Objekte zu machen. . .
Sie sollten beachten, dass, wenn der Parameter eine Objektreferenz ist, die Objektreferenz - nicht das Objekt selbst - "nach Wert" übergeben wird .
Und gegen Ende desselben Abschnitts macht er eine breitere Aussage darüber, dass Java nur als Wert und niemals als Referenz gilt.
Die Java-Programmiersprache übergibt Objekte nicht als Referenz. Es
übergibt Objektreferenzen nach Wert . Da sich zwei Kopien derselben Referenz auf dasselbe tatsächliche Objekt beziehen, sind Änderungen, die über eine Referenzvariable vorgenommen wurden, über die andere sichtbar. Es gibt genau einen Parameterübergabemodus - Übergabe nach Wert - und dies hilft, die Dinge einfach zu halten.
Dieser Abschnitt des Buches enthält eine ausführliche Erläuterung der Parameterübergabe in Java sowie der Unterscheidung zwischen Referenzübergabe und Wertübergabe und wird vom Ersteller von Java erstellt. Ich würde jeden ermutigen, es zu lesen, besonders wenn Sie immer noch nicht überzeugt sind.
Ich denke, der Unterschied zwischen den beiden Modellen ist sehr subtil. Wenn Sie nicht dort programmiert haben, wo Sie tatsächlich Pass-by-Reference verwendet haben, ist es leicht zu übersehen, wo sich zwei Modelle unterscheiden.
Ich hoffe, dass dies die Debatte regelt, aber wahrscheinlich nicht.
BEARBEITEN 3
Ich könnte ein wenig besessen von diesem Beitrag sein. Wahrscheinlich, weil ich das Gefühl habe, dass die Hersteller von Java versehentlich Fehlinformationen verbreitet haben. Wenn sie anstelle des Wortes "Referenz" für Zeiger etwas anderes verwendet hätten, beispielsweise Dingleberry, hätte es kein Problem gegeben. Man könnte sagen, "Java übergibt Dingleberries nach Wert und nicht nach Referenz", und niemand wäre verwirrt.
Dies ist der Grund, warum nur Java-Entwickler Probleme damit haben. Sie schauen sich das Wort "Referenz" an und glauben genau zu wissen, was das bedeutet, also machen sie sich nicht einmal die Mühe, das gegnerische Argument zu berücksichtigen.
Wie auch immer, ich habe in einem älteren Beitrag einen Kommentar bemerkt, der eine Ballon-Analogie ergab, die mir sehr gut gefallen hat. So sehr, dass ich mich entschied, einige Cliparts zusammenzukleben, um eine Reihe von Cartoons zu erstellen, um den Punkt zu veranschaulichen.
Übergeben einer Referenz als Wert - Änderungen an der Referenz werden nicht im Bereich des Aufrufers angezeigt, die Änderungen am Objekt jedoch. Dies liegt daran, dass die Referenz kopiert wird, aber sowohl das Original als auch die Kopie auf dasselbe Objekt verweisen.
Referenzübergabe - Es gibt keine Kopie der Referenz. Eine einzelne Referenz wird sowohl vom Aufrufer als auch von der aufgerufenen Funktion gemeinsam genutzt. Alle Änderungen an der Referenz oder den Daten des Objekts werden im Bereich des Anrufers berücksichtigt.
BEARBEITEN 4
Ich habe Beiträge zu diesem Thema gesehen, die die Implementierung der Parameterübergabe auf niedriger Ebene in Java beschreiben. Ich finde das großartig und sehr hilfreich, weil es eine abstrakte Idee konkretisiert. Für mich geht es jedoch eher um das in der Sprachspezifikation beschriebene Verhalten als um die technische Umsetzung des Verhaltens. Dies ist ein Auszug aus der Java-Sprachspezifikation, Abschnitt 8.4.1 :
Wenn die Methode oder der Konstruktor aufgerufen wird (§15.12), initialisieren die Werte der tatsächlichen Argumentausdrücke neu erstellte Parametervariablen vom deklarierten Typ, bevor der Hauptteil der Methode oder des Konstruktors ausgeführt wird. Der in der DeclaratorId angezeigte Bezeichner kann als einfacher Name im Hauptteil der Methode oder des Konstruktors verwendet werden, um auf den formalen Parameter zu verweisen.
Das heißt, Java erstellt eine Kopie der übergebenen Parameter, bevor eine Methode ausgeführt wird. Wie die meisten Leute, die im College Compiler studiert haben, habe ich "The Dragon Book" verwendet, das DAS Compiler-Buch ist. Es enthält eine gute Beschreibung von "Call-by-Value" und "Call-by-Reference" in Kapitel 1. Die Call-by-Value-Beschreibung stimmt genau mit den Java-Spezifikationen überein.
Als ich in den 90er Jahren Compiler studierte, verwendete ich die erste Ausgabe des Buches von 1986, die Java um 9 oder 10 Jahre älter war. Ich bin jedoch gerade auf eine Kopie der 2. Ausgabe von 2007 gestoßen, in der tatsächlich Java erwähnt wird! Abschnitt 1.6.6 mit der Bezeichnung "Parameterübergabemechanismen" beschreibt die Parameterübergabe ziemlich gut. Hier ist ein Auszug unter der Überschrift "Call-by-Value", in dem Java erwähnt wird:
Beim Call-by-Value wird der Aktualparameter ausgewertet (wenn es sich um einen Ausdruck handelt) oder kopiert (wenn es sich um eine Variable handelt). Der Wert wird an der Stelle platziert, die zu dem entsprechenden formalen Parameter der aufgerufenen Prozedur gehört. Diese Methode wird in C und Java verwendet und ist eine häufige Option in C ++ sowie in den meisten anderen Sprachen.