Einen String per Referenz in Java übergeben?

Ich bin es gewohnt, in C folgendes zu tun:

 void main() { String zText = ""; fillString(zText); printf(zText); } void fillString(String zText) { zText += "foo"; } 

Und die Ausgabe ist:

 foo 

In Java scheint dies jedoch nicht zu funktionieren. Ich nehme an, weil das String Objekt kopiert und nicht von referenziert übergeben wird . Ich dachte Strings wären Objekte, die immer als Referenz weitergegeben werden.

Was geht hier vor sich?

   

Sie haben drei Möglichkeiten:

  1. Verwenden Sie einen StringBuilder:

     StringBuilder zText = new StringBuilder (); void fillString(StringBuilder zText) { zText.append ("foo"); } 
  2. Erstellen Sie eine Containerklasse und übergeben Sie eine Instanz des Containers an Ihre Methode:

     public class Container { public String data; } void fillString(Container c) { c.data += "foo"; } 
  3. Erstelle ein Array:

     new String[] zText = new String[1]; zText[0] = ""; void fillString(String[] zText) { zText[0] += "foo"; } 

Vom Standpunkt der performance ist der StringBuilder normalerweise die beste Option.

In Java wird nichts als Referenz übergeben . Alles wird nach Wert übergeben . Objektreferenzen werden nach Wert übergeben. Zusätzlich sind Strings unveränderlich . Wenn Sie also an den übergebenen String anhängen, erhalten Sie nur einen neuen String. Sie könnten einen Rückgabewert verwenden oder stattdessen einen StringBuffer übergeben.

Was passiert, ist, dass die Referenz wertmäßig übergeben wird, dh eine Kopie der Referenz wird übergeben. Nichts in Java wird als Referenz übergeben, und da eine Zeichenfolge unveränderlich ist, erstellt diese Zuweisung ein neues Zeichenfolgenobjekt, auf das die Kopie der Referenz verweist. Der ursprüngliche Verweis verweist immer noch auf die leere Zeichenfolge.

Dies wäre für jedes Objekt gleich, dh es würde in einer Methode auf einen neuen Wert gesetzt. Das folgende Beispiel macht nur das, was vor sich geht, aber die Verkettung eines Strings ist wirklich das Gleiche.

 void foo( object o ) { o = new Object( ); // original reference still points to old value on the heap } 

java.lang.String ist unveränderlich.

Ich hasse es, URLs einzufügen, aber https://docs.oracle.com/javase/10/docs/api/java/lang/String.html ist wichtig für Sie, um zu lesen und zu verstehen, wenn Sie in Java-Land sind.

Objekte werden als Referenz übergeben, Primitive werden nach Wert übergeben.

String ist kein Primitiv, es ist ein Objekt und es ist ein Spezialfall von Objekt.

Dies dient dem Speichern. In JVM gibt es einen String-Pool. Für jede erzeugte Zeichenkette wird JVM versuchen, festzustellen, ob die gleiche Zeichenkette im Zeichenketten-Pool existiert, und auf sie zeigen, wenn bereits eine Zeichenkette vorhanden war.

 public class TestString { private static String a = "hello world"; private static String b = "hello world"; private static String c = "hello " + "world"; private static String d = new String("hello world"); private static Object o1 = new Object(); private static Object o2 = new Object(); public static void main(String[] args) { System.out.println("a==b:"+(a == b)); System.out.println("a==c:"+(a == c)); System.out.println("a==d:"+(a == d)); System.out.println("a.equals(d):"+(a.equals(d))); System.out.println("o1==o2:"+(o1 == o2)); passString(a); passString(d); } public static void passString(String s) { System.out.println("passString:"+(a == s)); } } 

/* AUSGABE */

 a==b:true a==c:true a==d:false a.equals(d):true o1==o2:false passString:true passString:false 

the == überprüft die Speicheradresse (Referenz) und die equals prüft auf Inhalt (Wert)

String ist ein unveränderliches Objekt in Java. Sie können die StringBuilder-class wie folgt für die Aufgabe verwenden, die Sie ausführen möchten:

 public static void main(String[] args) { StringBuilder sb = new StringBuilder("hello, world!"); System.out.println(sb); foo(sb); System.out.println(sb); } public static void foo(StringBuilder str) { str.delete(0, str.length()); str.append("String has been modified"); } 

Eine weitere Option besteht darin, eine class mit einem String als Bereichsvariable (sehr entmutigt) wie folgt zu erstellen:

 class MyString { public String value; } public static void main(String[] args) { MyString ms = new MyString(); ms.value = "Hello, World!"; } public static void foo(MyString str) { str.value = "String has been modified"; } 

Strings werden per Referenz übergeben (genau genommen, wie Ed Swangren bemerkt, wird ein Verweis auf den String als Wert übergeben ), aber sie sind unveränderlich.

 zText += foo; 

ist äquivalent zu:

 zText = new String(zText + "foo"); 

Das heißt, es ändert die (lokale) Variable zText so, dass sie nun auf einen neuen Speicherort zeigt, in dem ein String mit dem ursprünglichen Inhalt von zText mit "foo" angehängt ist. Das ursprüngliche Objekt wird nicht geändert, und die lokale Variable zText der main() -Methode zText immer noch auf die ursprüngliche (leere) Zeichenfolge.

 class StringFiller { static void fillString(String zText) { zText += "foo"; System.out.println("Local value: " + zText); } public static void main(String[] args) { String zText = ""; System.out.println("Original value: " + zText); fillString(zText); System.out.println("Final value: " + zText); } } 

Drucke:

 Original value: Local value: foo Final value: 

Wenn Sie die Zeichenfolge ändern möchten, können Sie, wie angegeben, StringBuilder oder einen anderen Container (ein Array oder eine benutzerdefinierte Container-class) verwenden, der Ihnen eine zusätzliche Ebene der pointers-Indirektion bietet. Alternativ können Sie einfach den neuen Wert zurückgeben und ihn zuweisen:

 class StringFiller2 { static String fillString(String zText) { zText += "foo"; System.out.println("Local value: " + zText); return zText; } public static void main(String[] args) { String zText = ""; System.out.println("Original value: " + zText); zText = fillString(zText); System.out.println("Final value: " + zText); } } 

Drucke:

 Original value: Local value: foo Final value: foo 

Dies ist wahrscheinlich die Java-ähnliche Lösung im allgemeinen Fall – siehe das Effektive Java- Element “Favor immutability”.

Wie bereits erwähnt, bietet Ihnen StringBuilder jedoch oft eine bessere performance – wenn Sie eine große Anzahl von Befehlen haben, insbesondere in einer Schleife, verwenden Sie StringBuilder .

Versuchen Sie jedoch, unveränderliche Strings anstelle von veränderbaren StringBuilders weiterzugeben, wenn Sie können – Ihr Code wird einfacher zu lesen und wartbarer. Ziehen Sie in Betracht, Ihre Parameter final festzulegen und Ihre IDE so zu konfigurieren, dass Sie gewarnt werden, wenn Sie einen Methodenparameter einem neuen Wert zuweisen.

String ist eine spezielle class in Java. Es ist Thread Save was bedeutet “Sobald eine String-Instanz erstellt wird, wird der Inhalt der String-Instanz nie geändert”.

Hier ist was los ist

  zText += "foo"; 

Zuerst erhält der Java-Compiler den Wert der zText String-Instanz und anschließend eine neue String-Instanz, deren Wert zText mit dem Zusatz “foo” ist. Sie wissen also, warum die Instanz, auf die zText zeigt, nicht geändert wird. Es ist eine völlig neue Instanz. Tatsächlich ist sogar String “foo” eine neue String-Instanz. Für diese statement erstellt Java zwei String-Instanzen, eine ist “foo”, eine andere ist der Wert von zText append “foo”. Die Regel ist einfach: Der Wert der String-Instanz wird niemals geändert.

Für die Methode fillString können Sie einen StringBuffer als Parameter verwenden oder Sie können dies folgendermaßen ändern:

 String fillString(String zText) { return zText += "foo"; } 

Die Antwort ist einfach. In Java sind Strings unveränderlich. Daher ist es so, als würde man den ‘final’-Modifikator (oder’ const ‘in C / C ++) verwenden. Also, einmal zugewiesen, können Sie es nicht so ändern, wie Sie es getan haben.

Sie können den Wert ändern , auf den eine Zeichenfolge verweist, aber Sie können den tatsächlichen Wert, auf den diese Zeichenfolge gerade verweist, NICHT ändern.

Ie. String s1 = "hey" . Sie können s1 = "woah" , und das ist völlig in s1 = "woah" , aber Sie können den zugrunde liegenden Wert des Strings (in diesem Fall: “hey”) nicht ändern, nachdem er mit plusEquals usw. zugewiesen wurde . s1 += " whatup != "hey whatup" ).

Verwenden Sie dazu die StringBuilder- oder StringBuffer-classn oder andere veränderbare Container, und rufen Sie dann einfach .toString () auf, um das Objekt in eine Zeichenfolge zurück zu konvertieren.

Hinweis: Strings werden oft als Hash-Schlüssel verwendet, daher ist das ein Teil des Grundes, warum sie unveränderlich sind.

Zeichenfolgen sind in Java unveränderlich .

Dies funktioniert mit StringBuffer

 public class test { public static void main(String[] args) { StringBuffer zText = new StringBuffer(""); fillString(zText); System.out.println(zText.toString()); } static void fillString(StringBuffer zText) { zText .append("foo"); } } 

Verwenden Sie StringBuilder noch besser

 public class test { public static void main(String[] args) { StringBuilder zText = new StringBuilder(""); fillString(zText); System.out.println(zText.toString()); } static void fillString(StringBuilder zText) { zText .append("foo"); } } 

String ist in Java unveränderlich. Sie können ein vorhandenes Zeichenfolgenliteral / Objekt nicht ändern / ändern.

String s = “Hallo”; s = s + “hi”;

Hier wird die vorherige Referenz s durch die neue Referenz s ersetzt, die auf den Wert “HelloHi” zeigt.

Um Veränderlichkeit zu erreichen, haben wir StringBuilder und StringBuffer.

StringBuilder s = neu StringBuilder (); s.append (“Hallo”);

Dies hängt den neuen Wert “Hi” an die gleichen Referenzen an. //

Aaron Digulla hat die beste Antwort bisher. Eine Variante seiner zweiten Option besteht darin, die Wrapper- oder Container-class MutableObject der Commons Lang-Bibliothek Version 3+ zu verwenden:

 void fillString(MutableObject c) { c.setValue(c.getValue() + "foo"); } 

Sie speichern die Deklaration der Containerklasse. Der Nachteil ist eine Abhängigkeit von der commons lang lib. Aber die Lib hat ziemlich viele nützliche functionen und fast jedes größere Projekt, an dem ich gearbeitet habe, hat es benutzt.

Um ein Objekt (einschließlich String) als Referenz in Java zu übergeben, übergeben Sie es möglicherweise als Mitglied eines umgebenden Adapters. Eine Lösung mit einem generischen ist hier:

 import java.io.Serializable; public class ByRef implements Serializable { private static final long serialVersionUID = 6310102145974374589L; T v; public ByRef(T v) { this.v = v; } public ByRef() { v = null; } public void set(T nv) { v = nv; } public T get() { return v; } // ------------------------------------------------------------------ static void fillString(ByRef zText) { zText.set(zText.get() + "foo"); } public static void main(String args[]) { final ByRef zText = new ByRef(new String("")); fillString(zText); System.out.println(zText.get()); } }