Eine Referenz ist immer ein Wert, wenn sie dargestellt wird, unabhängig von der verwendeten Sprache.
Um einen Blick außerhalb des üblichen Rahmens zu werfen, betrachten wir Assembly oder das Speichermanagement auf niedriger Ebene. Auf der CPU-Ebene wird eine Referenz zu allem sofort zu einem Wert, wenn sie in den Speicher oder in eines der CPU-Register geschrieben wird. (Deshalb ist Zeiger eine gute Definition. Es ist ein Wert, der gleichzeitig einen Zweck hat).
Daten im Speicher haben einen Ort und an diesem Ort befindet sich ein Wert (Byte, Wort, was auch immer). In Assembly haben wir eine praktische Lösung, um einem bestimmten Ort (auch Variable genannt) einen Namen zu geben, aber beim Kompilieren des Codes ersetzt der Assembler einfach den Namen durch den festgelegten Ort, genauso wie Ihr Browser Domainnamen durch IP-Adressen ersetzt.
Technisch gesehen ist es im Grundlagenbereich in keiner Sprache technisch unmöglich, eine Referenz zu irgendetwas zu übergeben, ohne sie zu repräsentieren (wenn sie sofort zu einem Wert wird).
Angenommen, wir haben eine Variable Foo, ihr Ort befindet sich im 47. Byte im Speicher und ihr Wert beträgt 5. Wir haben eine andere Variable Ref2Foo, die sich im 223. Byte im Speicher befindet, und ihr Wert ist 47. Diese Ref2Foo könnte eine technische Variable sein, die nicht explizit vom Programm erstellt wurde. Wenn man sich nur die Zahlen 5 und 47 ohne weitere Informationen ansieht, sieht man nur zwei Werte. Wenn Sie sie als Referenzen verwenden, um zu 5
zu gelangen, müssen Sie folgendes tun:
(Name)[Ort] -> [Wert am Ort]
---------------------
(Ref2Foo)[223] -> 47
(Foo)[47] -> 5
So funktionieren Sprungtabellen.
Wenn wir eine Methode/Funktion/Prozedur mit dem Wert von Foo aufrufen möchten, gibt es einige mögliche Möglichkeiten, die Variable an die Methode zu übergeben, je nach Sprache und ihren verschiedenen Methoden-Aufrufmodi:
- 5 wird in eines der CPU-Register kopiert (z. B. EAX).
- 5 wird auf den Stapel (Stack) gepusht.
- 47 wird in eines der CPU-Register kopiert.
- 47 wird auf den Stapel (Stack) gepusht.
- 223 wird in eines der CPU-Register kopiert.
- 223 wird auf den Stapel (Stack) gepusht.
In allen oben genannten Fällen wurde ein Wert - eine Kopie eines vorhandenen Werts - erstellt, nun liegt es an der empfangenden Methode, damit umzugehen. Wenn Sie in der Methode "Foo" schreiben, wird sie entweder aus EAX ausgelesen oder automatisch dereferenziert oder doppelt dereferenziert, der Prozess hängt davon ab, wie die Sprache funktioniert und/oder was der Typ von Foo vorgibt. Dies ist für den Entwickler verborgen, bis er den Dereferenzierungsprozess umgeht. Eine Referenz ist also ein Wert, wenn sie dargestellt wird, weil eine Referenz ein Wert ist, der verarbeitet werden muss (auf Sprachebene).
Jetzt haben wir Foo an die Methode übergeben:
- In Fall 1. und 2., wenn Sie Foo ändern (
Foo = 9
), betrifft dies nur den lokalen Bereich, da Sie eine Kopie des Werts haben. Von innerhalb der Methode aus können wir nicht einmal feststellen, wo im Speicher das originale Foo sich befand.
- In Fall 3. und 4., wenn Sie die Standard-Sprachkonstrukte verwenden und Foo ändern (
Foo = 11
), könnte sich Foo global ändern (das hängt von der Sprache ab, z. B. Java oder ähnlich wie bei Pascals procedure findMin(x, y, z: integer;
var m: integer);
). Allerdings, wenn die Sprache es Ihnen erlaubt, den Dereferenzierungsprozess zu umgehen, können Sie 47
ändern, sagen wir zu 49
. Zu diesem Zeitpunkt scheint es, als ob Foo geändert wurde, wenn man es liest, weil Sie den lokalen Zeiger darauf geändert haben. Und wenn Sie dieses Foo innerhalb der Methode ändern würden (Foo = 12
), würden Sie wahrscheinlich die Ausführung des Programms durcheinander bringen (ein sogenannter Segfault), weil Sie in einen anderen Speicher schreiben würden als erwartet, Sie könnten sogar einen Bereich ändern, der dazu bestimmt ist, ausführbaren Code zu halten, und das Schreiben darin würde den laufenden Code ändern (Foo befindet sich jetzt nicht mehr an der Stelle 47
). ABER der Wert von Foo, 47
, hat sich nicht global geändert, nur der in der Methode, da 47
auch eine Kopie in der Methode war.
- In Fall 5. und 6., wenn Sie
223
in der Methode ändern, entsteht das gleiche Chaos wie in 3. oder 4. (ein Zeiger, der auf einen nun ungültigen Wert zeigt, der wieder als Zeiger verwendet wird), aber das ist immer noch ein lokales Problem, da 223 kopiert wurde. Wenn Sie jedoch in der Lage sind, Ref2Foo
zu dereferenzieren (also 223
), zu erreichen und den Zeigerwert 47
zu ändern, sagen wir zu 49
, wird sich Foo global ändern, weil in diesem Fall die Methoden eine Kopie von 223
erhalten haben, aber das referenzierte 47
existiert nur einmal, und die Änderung auf 49
wird dazu führen, dass jede doppelte Dereferenzierung von Ref2Foo
auf einen falschen Wert verweist.
Wenn wir uns an belanglosen Details aufhalten, selbst Sprachen, die per Referenz übergeben, übergeben Werte an Funktionen, aber diese Funktionen wissen, dass sie sie für Dereferenzierungszwecke verwenden müssen. Dieses Weitergeben der Referenz als Wert wird dem Programmierer nur verborgen, da es praktisch nutzlos ist und die Terminologie nur Referenzübergabe ist.
Strenges Wertübergabe ist ebenfalls nutzlos, da es bedeuten würde, dass ein 100 Mbyte-Array jedes Mal kopiert werden müsste, wenn wir eine Methode mit dem Array als Argument aufrufen, deshalb kann Java nicht streng wertübergeben sein. Jede Sprache würde eine Referenz zu diesem riesigen Array übergeben (als Wert) und entweder einen Kopierschreibmechanismus verwenden, wenn dieses Array lokal in der Methode geändert werden kann, oder der Methode (wie Java es tut) erlauben, das Array global zu ändern (aus der Sicht des Aufrufers), und einige Sprachen erlauben, den Wert der Referenz selbst zu ändern.
Kurz gesagt und in der eigenen Terminologie von Java ausgedrückt, ist Java wertübergeben, wobei der Wert entweder ein echter Wert oder ein Wert ist, der eine Referenz darstellt.
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.