Ich habe gerade bemerkt, dass du dich auf meinen Artikel bezogen hast.
Das Java-Spezifikation besagt, dass in Java alles nach Wert übergeben wird. Es gibt kein "by-reference"-Konzept in Java.
Der Schlüssel zum Verständnis hierbei ist, dass etwas wie
Dog myDog;
kein Dog ist; es handelt sich tatsächlich um einen Pointer zu einem Dog. Die Verwendung des Begriffs "Referenz" in Java ist irreführend und führt hier zu den meisten Missverständnissen. Was sie als "Referenzen" bezeichnen, verhalten sich/fühlen sich eher wie das, was wir in den meisten anderen Sprachen "Pointer" nennen würden.
Das bedeutet, dass, wenn du hast
Dog myDog = new Dog("Rover");
foo(myDog);
du im Wesentlichen die Adresse des erstellten Dog
-Objekts an die Methode foo
übergibst.
(Ich sage im Wesentlichen, weil Java-Pointer/Referenzen keine direkten Adressen sind, aber es ist am einfachsten, sie so zu betrachten.)
Angenommen, das Dog
-Objekt befindet sich an der Speicheradresse 42. Das bedeutet, wir übergeben 42 an die Methode.
Wenn die Methode definiert wäre als
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
schaun wir mal, was passiert.
- der Parameter
someDog
wird auf den Wert 42 gesetzt
- bei "AAA"
someDog
wird zum Dog
verfolgt, auf den er zeigt (das Dog
-Objekt an Adresse 42)
- dieser
Dog
(der an Adresse 42) wird gebeten, seinen Namen in Max zu ändern
- bei "BBB"
- ein neuer
Dog
wird erstellt. Nehmen wir an, er ist an Adresse 74
- wir weisen den Parameter
someDog
zu 74 zu
- bei "CCC"
someDog
wird zum Dog
verfolgt, auf den er zeigt (das Dog
-Objekt an Adresse 74)
- dieser
Dog
(der an Adresse 74) wird gebeten, seinen Namen in Rowlf zu ändern
- dann kehren wir zurück
Jetzt denken wir darüber nach, was außerhalb der Methode passiert:
Hat sich myDog
verändert?
Das ist der Schlüssel.
Im Gedächtnis behalten, dass myDog
ein Pointer ist und kein tatsächlicher Dog
, lautet die Antwort NEIN. myDog
hat immer noch den Wert 42; er zeigt immer noch auf den originalen Dog
(aber beachte, dass aufgrund von Zeile "AAA" sein Name jetzt "Max" ist - immer noch der gleiche Dog; der Wert von myDog
hat sich nicht geändert.)
Es ist vollkommen gültig, einer Adresse zu folgen und das Ende davon zu ändern; das ändert jedoch nicht die Variable.
Java funktioniert genau wie C. Du kannst einen Pointer zuweisen, den Pointer an eine Methode übergeben, dem Pointer in der Methode folgen und die Daten ändern, auf die verwiesen wird. Der Aufrufer wird jedoch keine Änderungen sehen, die du an der Position machst, auf die der Pointer zeigt. (In einer Sprache mit by-reference-Semantik kann die Methode den Pointer ändern und der Aufrufer wird diese Änderung sehen.)
In C++, Ada, Pascal und anderen Sprachen, die by-reference unterstützen, kannst du tatsächlich die übergebene Variable ändern.
Wenn Java by-reference-Semantik hätte, würde die von uns definierte foo
-Methode oben die Position ändern, auf die myDog
zeigt, als sie someDog
in Zeile BBB zugewiesen hat.
Denke an Referenzparameter als Aliase für die übergebene Variable. Wenn dieser Alias zugewiesen wird, wird also auch die übergebene Variable zugewiesen.
Aktualisierung
Einige Diskussionen in den Kommentaren erfordern eine Klärung...
In C kannst du folgendes schreiben
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
int x = 1;
int y = 2;
swap(&x, &y);
Dies ist kein spezieller Fall in C. Beide Sprachen verwenden Pass-by-Value-Semantik. Hier erstellt die Aufrufstelle zusätzliche Datenstrukturen, um der Funktion den Zugriff und die Manipulation der Daten zu ermöglichen.
Der Funktion werden Zeiger auf die Daten übergeben, und sie folgt diesen Zeigern, um auf die Daten zuzugreifen und sie zu modifizieren.
Ein ähnlicher Ansatz in Java, wo der Aufrufer eine unterstützende Struktur einrichtet, könnte sein:
void swap(int[] x, int[] y) {
int temp = x[0];
x[0] = y[0];
y[0] = temp;
}
int[] x = {1};
int[] y = {2};
swap(x, y);
(oder wenn du beide Beispiele zeigen möchtest, die Funktionen der anderen Sprache nicht haben, erstelle eine änderbare IntWrapper-Klasse anstelle der Arrays)
In diesen Fällen simulieren sowohl C als auch Java Pass-by-Reference. Beide übergeben immer noch Werte (Zeiger auf ints oder Arrays) und folgen diesen Zeigern innerhalb der aufgerufenen Funktion, um die Daten zu manipulieren.
Pass-by-Reference bezieht sich auf die Funktionsdeklaration/-definition und wie sie ihre Parameter behandelt. Referenzsemantik gilt für jeden Aufruf dieser Funktion, und die Aufrufstelle muss nur Variablen übergeben, ohne zusätzliche Datenstruktur.
Diese Simulationen erfordern die Zusammenarbeit von aufrufender Stelle und Funktion. Sicherlich ist es nützlich, aber es ist immer noch Pass-by-Value.
6 Stimmen
Wir würden eher sagen, dass eine Variable, die "per Referenz übergeben" wird, verändert werden kann. Der Begriff taucht in Lehrbüchern auf, weil Sprachtheoretiker eine Möglichkeit brauchten, um zu unterscheiden, wie Sie primitive Datentypen (int, bool, byte) von komplexen und strukturierten Objekten (Array, Streams, Klasse) behandeln - das heißt, die möglicherweise ungebundene Speicherzuweisung.
6 Stimmen
Ich möchte darauf hinweisen, dass Sie sich in den meisten Fällen keine Gedanken darüber machen müssen. Ich habe viele Jahre lang Java programmiert, bis ich C ++ gelernt habe. Bis zu diesem Zeitpunkt hatte ich keine Ahnung, was Übergabe nach Referenz und Übergabe nach Wert sind. Die intuitive Lösung hat immer für mich funktioniert, deshalb ist Java eine der besten Sprachen für Anfänger. Wenn Sie sich also gerade Sorgen machen, ob Ihre Funktion eine Referenz oder einen Wert benötigt, übergeben Sie sie einfach so, wie sie ist, und es wird alles in Ordnung sein.
73 Stimmen
Java übergibt die Referenz nach Wert.
21 Stimmen
Ganz knapp gesagt entsteht diese Verwirrung, weil in Java alle Nicht-Primitive Datentypen durch Referenzen behandelt / zugegriffen werden. Allerdings wird immer nach Wert übergeben. Daher wird für alle Nicht-Primitive Typen die Referenz nach seinem Wert übergeben. Auch alle primitiven Typen werden nach Wert übergeben.
6 Stimmen
Ich fand das ziemlich hilfreich: baeldung.com/java-pass-by-value-or-pass-by-reference
1 Stimmen
Es ist der Unterschied zwischen foo.bar(sth) und foo = sth. Im ersten Fall wird das Objekt mit der Variablen, die darauf zeigt, geändert, und die Variable selbst, die auf das Objekt zeigt, wurde nicht geändert. Im zweiten Fall hingegen wurde die Variable selbst, die auf das Objekt zeigt, geändert und zeigt nun auf ein anderes Objekt. Wenn Sie einen Hintergrund in C++ haben: Ein Zeiger ist eine Variable, die eine Speicheradresse enthält, während eine Referenz die gleiche Speicheradresse wie das von ihr referenzierte Element hat. In Java ist tatsächlich ein Zeiger nach Wert übergeben, aber Javaistas nennen es Referenz!
3 Stimmen
Was würde es also bedeuten, wenn eine Sprache eine Referenz nicht nach Wert übergeben würde? Pointer auf Pointer übergeben? Gibt es Sprachen, die so etwas tun? Geben Sprachen wie C/C++ nicht auch Referenzen und Pointer nach Wert weiter??
1 Stimmen
Da Java eine objektorientierte Sprache ist und Objekte per Referenz übergeben werden, entsprechen Änderungen des Objekts gemäß langjährig etablierter Semantik dem Original. Es ist nicht klar, wie sich dieser "pass-by-value"-Unsinn so weit verbreiten konnte.
2 Stimmen
@SamGinrich Java ist immer durch Wertübertragung. Wenn Sie dieses grundlegende Konzept nicht verstehen, das bei weitem nicht so "unsinnig" ist, wie Sie es nennen, empfehle ich Ihnen, die am meisten hoch bewerteten Antworten hier zu lesen, bis Sie verstehen, warum.
0 Stimmen
Für diejenigen, die eine Videoerklärung bevorzugen youtu.be/fL-nXdKWwOg
1 Stimmen
Diese "Objekte werden nach Wert übergeben"-Sache ist irreführend. Referenzen zu Objekten werden übergeben. Nicht kopierte/verschobene Werte von Objekten. Dass die Referenzen selbst nach Wert übergeben werden, ist ein winziges Detail von nicht viel Interesse.
0 Stimmen
@Dalija Prasnikar ... diese Muggelsprache allein! Java ist im Grunde genommen eine objektorientierte Programmiersprache, kein Modus zum Übergeben von Objekten. Selbst wenn Java keine Dereferenzierung unterstützt, ist die Idee, sie als Grundlage für konzeptionelle Diskussionen zu nehmen, genauso fruchtbar wie wenn ein Vegetarier eine Rede bei einer Metzgergilde hält.
1 Stimmen
@SamGinrich Du mischst Äpfel und Birnen. Die Programmiersprachenparadigma allein ist nicht direkt mit den Mechanismen verbunden, die eine Sprache zur Parameterübergabe verwendet. Jeder, der die Funktionsweise von Compilern und Sprachenentwicklung kennt, weiß den Unterschied. Die Terminologie ist universell, weil sie denjenigen, die wissen, was sie bedeutet, eine Bedeutung vermittelt. Nur weil du es ablehnst, die Definition zu lernen, weil es dir keinen Sinn ergibt, bedeutet das nicht, dass es keinen Wert für andere hat, die es verstehen. Ich schlage vor, dass du die beste Antwort sehr sorgfältig liest.
0 Stimmen
@Dalija Prasnikar Sie sind sehr weise und verständnisvoll und haben die Definition, die Ihnen gegeben wurde, fleißig gelernt.
1 Stimmen
@SamGinrich Es geht nicht darum, die Definition wie ein Papagei auswendig zu lernen. Es geht darum zu verstehen, was sie bedeutet und warum solche Definitionen benötigt werden. Definitionen beziehen sich nicht nur auf eine einzige Sprache, sondern auf alle Sprachen. Sie sind wichtig, weil sie das genaue Verhalten in einfachen Worten beschreiben. Wenn man die Worte verdreht, verliert man die präzise Beschreibung dessen, was passiert. Es ist wie in der Mathematik, wo wir einige Definitionen haben, die die Grundlage für alles bilden. Man könnte sagen, dass einige willkürlich gewählt sind und wir die Mathematik auch aus leicht unterschiedlichen begriffen entwickeln könnten, aber dann würde auch alles andere in der Mathematik sich ändern.
0 Stimmen
@Dalija Prasnikar Wie ich oben sagte: "Java ist keine gute Basis, um Dereferenzierung zu verstehen"; trotzdem sind Sie frei, dies zu behaupten. Ich wäre vorsichtig, jemanden als ungebildet zu betrachten, der eine konsistente Meinung zu einem Thema hat.
1 Stimmen
@SamGinrich Konsequenz bedeutet nicht automatisch Wissen. Du kannst konsequent behaupten, dass die Erde flach ist, und du würdest immer noch falsch liegen.
4 Stimmen
Bevor du eine neue Antwort postest, bedenke, dass es bereits 93+ Antworten auf diese Frage gibt. Stelle sicher, dass deine Antwort Informationen enthält, die nicht unter den bereits vorhandenen Antworten zu finden sind. Danke!
0 Stimmen
@Dalija Prasnikar Das Dogma "Java ist pass-by-value" ist ziemlich konsistent, solange man nicht die Grammatik betrachtet. Ich habe gesehen, dass du dich frei gefühlt hast, Beiträge zu löschen, die dieser Regel nicht folgen, dies ist keine Haltung, die die Absicht einer Diskussion widerspiegelt, die offen für Kritik ist. Es gibt gebildete Menschen mit anderen Denkweisen, auch zum Thema. Es sollten bessere Gründe für die Zensur geben und ich hoffe, dass du sie findest.
1 Stimmen
@SamGinrich Meine Moderationsaktivitäten basieren nicht darauf, was mir gefällt oder nicht, sondern auf Korrektheit. Wenn du jedoch Probleme mit den Handlungen einer Person hast oder glaubst, dass ein Beitrag nicht angemessen behandelt wurde, kannst du das immer im Stack Overflow Meta ansprechen. Was diese Frage/Antwort betrifft, habe ich nichts Neues zu sagen, was ich nicht schon gesagt habe.