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?
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.
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:
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.
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%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%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.
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.
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.
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.
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).
@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.
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%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.
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%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:
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:
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.
Tags und Links java pass-by-value pass-by-reference