Kann mir jemand erklären, was die Gründe für die Weitergabe von "Wert" und nicht von "Referenz" in Java sind?

8

Ich bin ziemlich neu in Java (seit Jahren schreibe ich andere Sachen) und wenn ich nicht etwas verpasse (und ich bin glücklich, hier falsch zu liegen), ist das Folgende ein fataler Fehler ...

%Vor%

Nun bin ich mir der (ziemlich schlecht formulierten) Vorstellung bewusst, dass in Java alles an "Wert" und nicht "Referenz" weitergegeben wird, aber String ist ein Objekt und hat alle Arten von Schnickschnack. man würde erwarten, dass im Gegensatz zu einem int ein Benutzer in der Lage wäre, an der Sache zu arbeiten, die an die Methode übergeben wurde (und nicht mit dem durch die überladene = gesetzten Wert hängen bleibt).

Kann mir jemand erklären, was die Gründe für diese Designentscheidung waren? Wie gesagt, ich möchte nicht hier sein, und vielleicht vermisse ich etwas Offensichtliches?

    
Genia S. 05.02.2009, 03:51
quelle

15 Antworten

11

Diese Tirade erklärt es besser als ich jemals versuchen könnte:

  

In Java werden Grundelemente nach Wert übergeben. Objekte sind jedoch nicht   per Referenz weitergegeben. Eine korrekte Anweisung wäre Objektreferenzen   werden nach Wert übergeben.

    
Ric Tokyo 05.02.2009, 03:59
quelle
9

Wenn Sie "foo" übergeben, übergeben Sie die Referenz als Wert an ThisDoesntWork () an "foo". Das bedeutet, dass Sie bei der Zuweisung von "foo" innerhalb Ihrer Methode lediglich die Referenz einer lokalen Variablen (foo) als Verweis auf Ihre neue Zeichenfolge festlegen.

Wenn Sie darüber nachdenken, wie sich Zeichenketten in Java verhalten, sollten Sie daran denken, dass Zeichenketten unveränderlich sind. Es funktioniert in C # genauso und aus guten Gründen:

  • Sicherheit : Niemand kann Daten in Ihre Zeichenfolge stecken und einen Pufferüberlauffehler verursachen, wenn niemand ihn ändern kann!
  • Geschwindigkeit : Wenn Sie sicher sein können, dass Ihre Strings unveränderlich sind, wissen Sie, dass die Größe immer gleich ist und Sie die Datenstruktur nicht immer im Speicher verschieben müssen, wenn Sie sie manipulieren. Sie (der Sprachdesigner) müssen sich auch keine Gedanken darüber machen, den String als langsame verkettete Liste zu implementieren. Dies geht jedoch in beide Richtungen. Das Anhängen von Zeichenfolgen mit dem Operator + kann teuer sein und Sie müssen ein StringBuilder-Objekt verwenden, um dies auf eine leistungsfähige, speichereffiziente Art und Weise zu tun.

Nun zu Ihrer größeren Frage. Warum werden Objekte auf diese Weise weitergegeben? Nun, wenn Java Ihre Zeichenfolge als "Wert" deklariert hat, müsste es tatsächlich die gesamte Zeichenfolge kopieren, bevor es an Ihre Funktion übergeben wird. Das ist ziemlich langsam. Wenn es den String als Referenz übergeben hat und Sie es ändern können (wie bei C), haben Sie die Probleme, die ich gerade aufgelistet habe.

    
Dave Markle 05.02.2009 03:55
quelle
5

Da meine ursprüngliche Antwort war "Warum ist es passiert" und nicht "Warum wurde die Sprache entworfen, so dass es passiert ist", werde ich dies ein weiteres Mal geben.

Um die Dinge zu vereinfachen, werde ich den Methodenaufruf loswerden und zeigen, was auf andere Weise passiert.

%Vor%

Damit die letzte Anweisung "Hallo" ausgibt, muss b auf dasselbe "Loch" im Speicher zeigen, auf das a zeigt (ein Zeiger). Dies ist, was Sie wollen, wenn Sie als Referenz weitergeben möchten. Es gibt ein paar Gründe, weshalb Java sich entschieden hat, nicht in diese Richtung zu gehen:

  • Zeiger sind verwirrend Die Entwickler von Java haben versucht, einige der verwirrenderen Dinge in anderen Sprachen zu entfernen. Zeiger sind eines der am meisten missverstandenen und falsch verwendeten Konstrukte von C / C ++ zusammen mit Überladen von Operatoren.

  • Zeiger sind Sicherheitsrisiken Zeiger verursachen viele Sicherheitsprobleme, wenn sie missbraucht werden. Ein böswilliges Programm ordnet diesem Teil des Speichers etwas zu, dann ist das, was Sie für Ihr Objekt hielten, tatsächlich das eines anderen. (Java hat bereits das größte Sicherheitsproblem beseitigt, Pufferüberläufe mit überprüften Arrays)

  • Abstraktionsleckage Wenn Sie anfangen, sich mit "Was ist in Erinnerung und wo?" genau zu befassen, wird Ihre Abstraktion weniger eine Abstraktion. Während Abstraktionsleckage mit Sicherheit in eine Sprache kriecht, wollten die Designer sie nicht direkt backen.

  • Objekte sind alles, was Sie interessieren In Java ist alles ein Objekt, nicht der Raum, den ein Objekt einnimmt. Hinzufügen von Zeigern würde den Raum, den ein Objekt einnimmt, wichtig machen, obwohl .......

Sie könnten emulieren, was Sie wollen, indem Sie ein "Loch" -Objekt erstellen. Sie könnten sogar Generika verwenden, um es sicher zu machen. Zum Beispiel:

%Vor%     
Laplie Anderson 05.02.2009 14:38
quelle
4

Ihre gestellte Frage hat nicht wirklich damit zu tun, dass Sie den Wert übergeben, indem Sie auf einen Verweis verweisen oder dass Strings unveränderlich sind (wie von anderen angegeben).

Innerhalb der Methode erstellen Sie tatsächlich eine lokale Variable (ich nenne diese "localFoo"), die auf die gleiche Referenz verweist wie Ihre ursprüngliche Variable ("originalFoo").

Wenn Sie localfoo "howdy" zuweisen, ändern Sie nicht, wo originalFoo zeigt.

Wenn Sie etwas wie:

getan haben %Vor%

Würden Sie Folgendes erwarten:

%Vor%

um "grüß dich" auszudrucken? Es druckt "" aus.

Sie können nicht ändern, worauf originalFoo zeigt, indem Sie ändern, auf was localFoo zeigt. Sie können das Objekt ändern, auf das beide zeigen (wenn es nicht unveränderlich ist). Zum Beispiel

%Vor%     
Laplie Anderson 05.02.2009 04:13
quelle
3

In Java werden alle übergebenen Variablen von value-even-Objekten übergeben. Alle an eine Methode übergebenen Variablen sind tatsächlich Kopien des ursprünglichen Wertes. Im Fall Ihres String-Beispiels wird der Original-Zeiger (der eigentlich eine Referenz ist - um Verwirrung zu vermeiden, aber ein anderes Wort verwendet) in eine neue Variable kopiert, die der Parameter der Methode wird.

Es wäre ein Schmerz, wenn alles durch Bezugnahme wäre. Man müsste überall private Kopien machen, was definitiv ein echter Schmerz wäre. Jeder weiß, dass die Verwendung von Unveränderlichkeit für Werttypen usw. Ihre Programme unendlich einfacher und besser skalierbar macht.

Einige Vorteile umfassen: - Keine Notwendigkeit, defensive Kopien zu machen. - Threadsafe - keine Notwendigkeit, sich um das Sperren zu kümmern, falls jemand anderes das Objekt ändern möchte.

    
mP. 05.02.2009 04:07
quelle
2

Das Problem besteht darin, dass Sie einen Java-Referenztyp instanziieren. Dann übergeben Sie diesen Referenztyp an eine statische Methode und weisen sie einer lokal begrenzten Variablen zu.

Es hat nichts mit Unveränderlichkeit zu tun. Genau dasselbe wäre für einen veränderlichen Referenztyp passiert.

    
Julien Chastang 05.02.2009 04:06
quelle
1

Wenn wir eine grobe C- und Assembler-Analogie machen würden:

%Vor% %Vor%

Ein möglicher Grund, warum in Java alles an Wert übergeben wird, ist, dass seine Sprachentwickler die Sprache vereinfachen und alles in OOP-Manier machen wollen.

Sie würden lieber einen Integer-Swapper mit Objekten entwerfen, als sie eine erstklassige Unterstützung für die Referenz-Weitergabe bieten, das gleiche für den Delegaten (Gosling fühlt sich eklig mit dem Zeiger zur Funktion, er würde diese Funktionalität lieber auf Objekte stopfen) und enum.

Sie vereinfachen die Sprache (alles ist ein Objekt) zu Lasten einer nicht erstklassigen Unterstützung für die meisten Sprachkonstrukte, z. Durch Bezugnahme kommt Delegierten, enum, Eigenschaften in den Sinn.

    
Michael Buen 05.02.2009 06:05
quelle
0

Sind Sie sicher, dass es null druckt? Ich denke, es wird nur leer sein, als wenn Sie die Foo-Variable initialisiert haben, die Sie leere Zeichenfolge bereitgestellt haben.

Das Zuweisen von foo in der thisDoesntWork-Methode ändert nicht die Referenz der in der Klasse definierten foo-Variablen, so dass foo in System.out.println (foo) immer noch auf das alte leere String-Objekt zeigt.

    
Bhushan Bhangale 05.02.2009 03:59
quelle
0

Dave, du musst mir vergeben (nun, ich denke, du musst nicht "müssen", aber ich hätte es lieber getan), aber diese Erklärung ist nicht übermäßig überzeugend. Die Sicherheitsgewinne sind ziemlich minimal, da jeder, der den Wert der Zeichenfolge ändern muss, einen Weg findet, dies mit einer hässlichen Problemumgehung zu tun. Und Geschwindigkeit ?! Sie selbst (ganz richtig) behaupten, dass das ganze Geschäft mit dem + extrem teuer ist.

Der Rest von euch, bitte versteht, dass ich weiß, wie es funktioniert, ich frage, warum es so funktioniert ... bitte hört auf, den Unterschied zwischen den Methoden zu erklären.

(und ich suche wirklich keinen Kampf hier, übrigens, ich sehe einfach nicht, dass das eine rationale Entscheidung war).

    
Genia S. 05.02.2009 04:13
quelle
0

@Axelle

Kennen Sie den Unterschied zwischen Wert- und Referenzwerten?

In Java werden sogar Referenzen nach Wert übergeben. Wenn Sie einen Verweis auf ein Objekt übergeben, erhalten Sie eine Kopie des Referenzzeigers in der zweiten Variablen. Tahts warum die zweite Variable geändert werden kann, ohne die erste zu beeinflussen.

    
mP. 05.02.2009 05:49
quelle
0

Das liegt daran, dass es innerhalb der Methode eine lokale Variable erzeugt. Was wäre ein einfacher Weg (von dem ich mir sicher bin, dass es funktionieren würde):

%Vor%     
lavamunky 05.02.2009 09:11
quelle
0

Wenn Sie an ein Objekt als nur die Felder im Objekt denken, werden Objekte in Java als Referenz übergeben, da eine Methode die Felder eines Parameters ändern kann und ein Aufrufer die Änderung beobachten kann. Wenn Sie jedoch auch ein Objekt als Identität betrachten, werden Objekte als Wert übergeben, da eine Methode die Identität eines Parameters nicht auf eine Weise ändern kann, die der Aufrufer beobachten kann. Also würde ich sagen, Java ist pass-by-value.

    
drscroogemcduck 05.02.2009 09:36
quelle
0

Dies liegt daran, dass Sie innerhalb von "thisDoesntWork" effektiv den lokalen Wert von foo zerstören. Wenn Sie auf diese Weise durch Verweis übergeben möchten, kann der String immer in einem anderen Objekt, sagen wir in einem Array, eingekapselt werden.

%Vor%

Ergebnisse in der folgenden Ausgabe:

%Vor%     
Nate 05.02.2009 16:41
quelle
-1

Referenztyp-Argumente werden als Referenzen auf Objekte selbst übergeben (keine Referenzen auf andere Variablen , die sich auf Objekte beziehen). Sie können Methoden für das Objekt aufrufen, das übergeben wurde. In Ihrem Codebeispiel jedoch:

%Vor%

Sie speichern nur einen Verweis auf den String "howdy" in einer Variablen, die für die Methode lokal ist. Diese lokale Variable ( foo ) wurde beim Aufruf der Methode auf den Wert des foo des Aufrufers initialisiert, hat jedoch keinen Verweis auf die Variable des Aufrufers. Nach der Initialisierung:

%Vor%

Nach der Zuweisung in Ihrer Methode:

%Vor%

Sie haben da noch ein anderes Problem: String Instanzen sind unveränderbar (vom Design her für die Sicherheit), daher können Sie ihren Wert nicht ändern.

Wenn Sie wirklich möchten, dass Ihre Methode einen Anfangswert für Ihre Zeichenkette (oder für jede Zeit in ihrem Leben) liefert, dann lassen Sie Ihre Methode zurückgeben ein Wert String , den Sie der Variablen des Aufrufers zum Zeitpunkt des Aufrufs zuweisen. So etwas zum Beispiel:

%Vor%     
joel.neely 05.02.2009 04:06
quelle
-2

Machen Sie das wirklich große Tutorial auf der Sonnen-Website.

Sie scheinen den Unterschied, den Bereichsvariablen haben können, nicht zu verstehen. "foo" ist lokal für Ihre Methode. Nichts außerhalb dieser Methode kann ändern, was "foo" auch zeigt. Das auf Ihre Methode bezogene "foo" ist ein völlig anderes Feld - es ist ein statisches Feld auf Ihrer einschließenden Klasse.

Das Scoping ist besonders wichtig, da Sie nicht möchten, dass alles andere in Ihrem System sichtbar ist.

    
mP. 05.02.2009 03:56
quelle