8 Stimmen

Kann mir jemand erklären, was der Grund für die Übergabe durch "Wert" und nicht durch "Referenz" in Java ist?

Ich bin ein ziemlicher Neuling in Java (ich schreibe seit vielen Jahren andere Sachen), und wenn ich nicht gerade etwas übersehe (und ich freue mich, wenn ich hier falsch liege), ist das Folgende ein fataler Fehler...

String foo = new String();
thisDoesntWork(foo);
System.out.println(foo);//this prints nothing

public static void thisDoesntWork(String foo){
   foo = "howdy";
}

Nun, ich bin mir des (ziemlich schlecht formulierten) Konzepts bewusst, dass in Java alles als "Wert" und nicht als "Referenz" übergeben wird, aber String ist ein Objekt und hat alle Arten von Schnickschnack, so dass man erwarten würde, dass im Gegensatz zu einem int ein Benutzer in der Lage wäre, die Sache zu bearbeiten, die an die Methode übergeben wird (und nicht mit dem Wert feststeckt, der durch das überladene =).

Kann mir jemand erklären, was der Grund für diese Designentscheidung war? Wie gesagt, ich will hier nicht Recht haben, und vielleicht übersehe ich etwas Offensichtliches?

0 Stimmen

Ich bin mir nicht ganz sicher, was Sie damit sagen wollen. Bitte klären Sie es ein wenig?

0 Stimmen

Der vorherige Titel war nicht gut, denn es wäre unmöglich, den Inhalt Ihrer Frage über den Titel zu suchen.

0 Stimmen

Kein Problem, dieser Titel macht mehr Sinn :)

11voto

Ric Tokyo Punkte 6497

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

In Java werden Primitive als Wert übergeben. Objekte werden jedoch nicht per Referenz weitergegeben. Eine korrekte Aussage wäre: Objektreferenzen werden als Wert weitergegeben.

9voto

Dave Markle Punkte 91733

Wenn Sie "foo" übergeben, übergeben Sie die référence zu "foo" als Wert für ThisDoesntWork(). Das bedeutet, dass Sie bei der Zuweisung an "foo" innerhalb Ihrer Methode lediglich den Verweis auf eine lokale Variable (foo) als Verweis auf Ihre neue Zeichenfolge festlegen.

Eine weitere Sache, die man im Hinterkopf behalten sollte, wenn man darüber nachdenkt, wie sich Strings in Java verhalten, ist, dass Strings unveränderlich sind. In C# funktioniert das genauso, und zwar aus guten Gründen:

  • Sicherheit : Niemand kann Daten in Ihre Zeichenkette einspeisen und einen Pufferüberlauffehler verursachen, wenn niemand sie ändern kann!
  • Geschwindigkeit : Wenn Sie sicher sein können, dass Ihre Zeichenketten unveränderlich sind, wissen Sie, dass ihre Größe immer gleich bleibt, und Sie müssen die Datenstruktur im Speicher nicht verschieben, wenn Sie sie manipulieren. Sie (der Sprachdesigner) müssen sich auch keine Gedanken über die Implementierung der Zeichenkette als langsame verknüpfte Liste machen. Dies gilt allerdings in beide Richtungen. Das Anhängen von Strings mit dem Operator + kann sehr speicherintensiv sein, und Sie müssen ein StringBuilder-Objekt verwenden, um dies auf eine leistungsstarke und speichereffiziente Weise zu tun.

Nun zu Ihrer eigentlichen Frage. Warum werden Objekte auf diese Weise übergeben? Nun, wenn Java Ihre Zeichenkette als das übergeben würde, was Sie traditionell "by value" nennen würden, müsste es die gesamte Zeichenkette kopieren, bevor es sie an Ihre Funktion weitergibt. Das ist ziemlich langsam. Wenn die Zeichenkette als Referenz übergeben würde und Sie sie ändern könnten (wie in C), hätten Sie die Probleme, die ich gerade aufgeführt habe.

5voto

Laplie Anderson Punkte 6167

Da meine ursprüngliche Antwort lautete "Warum ist es passiert" und nicht "Warum wurde die Sprache so gestaltet, dass es passiert ist", werde ich es noch einmal versuchen.

Um die Sache zu vereinfachen, verzichte ich auf den Methodenaufruf und zeige, was auf andere Weise geschieht.

String a = "hello";
String b = a;
String b = "howdy"

System.out.print(a) //prints hello

Damit die letzte Anweisung "Hallo" ausgibt, b muss auf das gleiche "Loch" im Speicher verweisen, das a zeigt auf (ein Zeiger). Das ist es, was Sie wollen, wenn Sie eine Referenzübergabe wünschen. Es gibt eine Reihe von Gründen, warum Java sich gegen diesen Weg entschieden hat:

  • Zeiger sind verwirrend Die Entwickler von Java haben versucht, einige der verwirrenden Dinge anderer Sprachen zu beseitigen. Zeiger sind neben der Operatorüberladung eines der am meisten missverstandenen und unsachgemäß verwendeten Konstrukte von C/C++.

  • Zeiger sind Sicherheitsrisiken Zeiger verursachen viele Sicherheitsprobleme, wenn sie missbraucht werden. Ein bösartiges Programm weist diesem Teil des Speichers etwas zu, und was Sie für Ihr Objekt hielten, gehört in Wirklichkeit einem anderen. (Java hat das größte Sicherheitsproblem, Pufferüberläufe, bereits mit geprüften Arrays beseitigt).

  • Abstraktion Leckage Wenn man anfängt, sich mit der Frage "Was befindet sich wo im Speicher?" zu beschäftigen, wird die Abstraktion weniger abstrakt. Abstraktionslecks schleichen sich zwar mit Sicherheit in eine Sprache ein, aber die Entwickler wollten sie nicht direkt einbauen.

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

Sie könnten das, was Sie wollen, nachbilden, indem Sie ein "Hole"-Objekt erstellen. Sie könnten sogar Generika verwenden, um es typsicher zu machen. Zum Beispiel:

public class Hole<T> {
   private T objectInHole;

   public void putInHole(T object) {
      this.objectInHole = object;
   }
   public T getOutOfHole() {
      return objectInHole;
   }

   public String toString() {
      return objectInHole.toString();
   }
   .....equals, hashCode, etc.
}

Hole<String> foo = new Hole<String)();
foo.putInHole(new String());
System.out.println(foo); //this prints nothing
thisWorks(foo);
System.out.println(foo);//this prints howdy

public static void thisWorks(Hole<String> foo){
   foo.putInHole("howdy");
}

4voto

Laplie Anderson Punkte 6167

Ihre Frage wie gestellt hat nicht wirklich etwas mit der Übergabe von Werten, der Übergabe von Referenzen oder der Tatsache zu tun, dass Strings unveränderlich sind (wie andere bereits festgestellt haben).

Innerhalb der Methode erstellen Sie eine lokale Variable (ich nenne sie "localFoo"), die auf dieselbe Referenz wie Ihre ursprüngliche Variable ("originalFoo") verweist.

Wenn Sie localFoo "howdy" zuweisen, ändern Sie nicht, wohin originalFoo zeigt.

Wenn Sie etwas tun wie:

String a = "";
String b = a;
String b = "howdy"?

Würden Sie das erwarten?

System.out.print(a)

um "Hallo" auszudrucken? Er druckt "" aus.

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

List foo = new ArrayList();
System.out.println(foo.size());//this prints 0

thisDoesntWork(foo);
System.out.println(foo.size());//this prints 1

public static void thisDoesntWork(List foo){   
    foo.add(new Object);
}

3voto

mP. Punkte 17565

In Java werden alle Variablen, die übergeben werden, als Wert weitergegeben - auch Objekte. Alle Variablen, die an eine Methode übergeben werden, sind eigentlich Kopien des ursprünglichen Wertes. Im Fall Ihres String-Beispiels wird der ursprüngliche Zeiger (eigentlich eine Referenz - aber um Verwirrung zu vermeiden, verwende ich ein anderes Wort) in eine neue Variable kopiert, die zum Parameter der Methode wird.

Es wäre mühsam, wenn alles mit Verweisen versehen wäre. Man müsste überall Privatkopien anfertigen, was definitiv sehr mühsam wäre. Jeder weiß, dass die Verwendung von Unveränderlichkeit für Werttypen usw. Ihre Programme unendlich viel einfacher und skalierbarer macht.

Einige Vorteile sind: - Sie müssen keine Sicherheitskopien anfertigen. - Threadsicher - Sie müssen sich nicht um das Sperren kümmern, nur für den Fall, dass jemand anderes das Objekt ändern möchte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X