Was ist der Unterschied zwischen "Text" und neuem String ("Text")?


195

Was ist der Unterschied zwischen diesen beiden folgenden Aussagen?

String s = "text";

String s = new String("text");


Jeder bitte darauf antworten. String a = "Java"; String b = "Java"; System.out.println (a == b); true // aber System.out.println ("a == b?" + a == b); // false ...
Energy

Ich verstehe nicht, wann ich einen Kommentar hinzugefügt habe ("a == b?) => Mein Ergebnis wird FALSCH. Warum?
Energie

@Energy Das Ergebnis ist, falsedass die Reihenfolge der Operationen vorschreibt, dass der Operator + zuerst geht und "a == b?" mit a, um einen String "a == b? Java" zu erstellen. Dann wird der Ausdruck "a==b?Java" == bals falsch ausgewertet.
Allison B

@ AllisonB hat es verstanden, vielen Dank!
Energie

Antworten:


186

new String("text"); Erstellt explizit eine neue und referenziell unterschiedliche Instanz eines StringObjekts. String s = "text";kann eine Instanz aus dem String-Konstanten-Pool wiederverwenden , wenn eine verfügbar ist.

Sie würden den Konstruktor sehr selten jemals verwenden wollen new String(anotherString). Von der API:

String(String original): Initialisiert ein neu erstelltes String Objekt so, dass es dieselbe Zeichenfolge wie das Argument darstellt. Mit anderen Worten, die neu erstellte Zeichenfolge ist eine Kopie der Argumentzeichenfolge. Sofern keine explizite Kopie des Originals erforderlich ist, ist die Verwendung dieses Konstruktors nicht erforderlich, da Zeichenfolgen unveränderlich sind.

Verwandte Fragen


Was referentielle Unterscheidung bedeutet

Untersuchen Sie den folgenden Ausschnitt:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==Bei zwei Referenztypen erfolgt ein Referenzidentitätsvergleich. Zwei Objekte, equalsdie nicht unbedingt sind ==. Es ist normalerweise falsch, ==Referenztypen zu verwenden. Die meiste Zeit equalsmuss stattdessen verwendet werden.

Dennoch, wenn Sie aus irgendeinem Grund müssen zwei erstellen , equalsaber nicht ==String, Sie können die Verwendung new String(anotherString)Konstruktor. Es muss jedoch noch einmal gesagt werden, dass dies sehr eigenartig ist und selten die Absicht ist.

Verweise

Verwandte Themen


3
Wenn ich schreibe: String s = neuer String ("abc"); Und jetzt schreibe ich: String s = "abc"; Will String s = "abc"; ein neues String-Literal im String-Pool erstellen?
Kaveesh Kanwal

Warum beantwortet niemand die vorhergehende Frage?
Zeds

2
@KaveeshKanwal Nein, das Literal wird nicht dupliziert. Wie Sie sehen können, gibt es 2 "abc"s. Nur einer von ihnen geht in den String-Pool und der andere verweist darauf. Dann gibt ses das richtige neue Objekt.
Kayaman

1
@Kaveesh Kanwal - String s = neuer String ("abc") erstellt nur ein neues String-Objekt mit dem Wert "abc". Und die 2. Anweisung prüft, ob bereits ein "abc" -Stringliteral im Stringpool vorhanden ist oder nicht. Wenn bereits vorhanden, wird der Verweis auf das vorhandene zurückgegeben, und wenn nicht, wird ein neues Literal ("abc") im String-Pool erstellt. Hoffe, es löst Ihre Anfrage!
user968813

Es gibt kein "Mai". Der Compiler muss String-Literale bündeln. JLS 3.10.5 .
Marquis von Lorne

118

String- Literale werden in den String Constant Pool aufgenommen .

Der folgende Schnappschuss kann Ihnen helfen, ihn visuell zu verstehen und sich länger daran zu erinnern.

Geben Sie hier die Bildbeschreibung ein


Objekterstellung Zeile für Zeile:

String str1 = new String("java5");

Mit dem String-Literal "java5" im Konstruktor wird ein neuer String-Wert im String-Konstanten-Pool gespeichert. Mit dem neuen Operator wird im Heap ein neues Zeichenfolgenobjekt mit dem Wert "java5" erstellt.

String str2 = "java5"

Die Referenz "str2" zeigt auf den bereits gespeicherten Wert im String-Konstanten-Pool

String str3 = new String(str2);

Im Heap wird ein neues Zeichenfolgenobjekt mit demselben Wert wie die Referenz von "str2" erstellt.

String str4 = "java5";

Die Referenz "str4" zeigt auf den bereits gespeicherten Wert im String-Konstanten-Pool

Objekte insgesamt: Heap - 2, Pool - 1

Weitere Informationen zur Oracle-Community


1
Gute Antwort ... aber ich möchte wissen, dass ich jetzt den Wert von str1 = "java6" ändern werde, dann wird sich der Wert von str4 ändern?
CoronaPintu

2
Ja, ich hatte überprüft, es wird den Wert von str4 nicht ändern
CoronaPintu

@Braj Können Sie die Behauptung Ihrer Antwort dokumentieren?
Basil Bourque

@Braj: Sollen die Header für 'Heap' & 'Pool' in der Tabelle umgekehrt sein?
Rahul Kurup

Nicht richtig. Der konstante Pool wird zur Kompilierungszeit und nicht zur Ausführungszeit erstellt. Verwenden Sie die Anführungszeichenformatierung nicht für Text, der nicht in Anführungszeichen steht.
Marquis von Lorne

15

Man erstellt einen String im String Constant Pool

String s = "text";

Der andere erstellt eine Zeichenfolge im konstanten Pool ( "text") und eine weitere Zeichenfolge im normalen Heap-Bereich ( s). Beide Zeichenfolgen haben den gleichen Wert wie "Text".

String s = new String("text");

s geht dann verloren (berechtigt zur GC), wenn es später nicht verwendet wird.

String-Literale werden dagegen wiederverwendet. Wenn Sie "text"an mehreren Stellen Ihrer Klasse verwenden, handelt es sich tatsächlich um eine und nur eine Zeichenfolge (dh mehrere Verweise auf dieselbe Zeichenfolge im Pool).


Saiten im konstanten Pool gehen nie verloren. Wollten Sie sagen, dass 's' verloren geht, wenn es später nicht verwendet wird?
Marquis von Lorne

@EJP: Ja, ich meinte "s". Danke fürs bemerken. Ich werde die Frage korrigieren.

9

JLS

Das Konzept wird von der JLS als "interning" bezeichnet.

Relevante Passage aus JLS 7 3.10.5 :

Darüber hinaus bezieht sich ein String-Literal immer auf dieselbe Instanz der Klasse String. Dies liegt daran, dass Zeichenfolgenliterale - oder allgemeiner Zeichenfolgen, die die Werte konstanter Ausdrücke sind (§15.28) - mit der Methode String.intern "interniert" werden, um eindeutige Instanzen gemeinsam zu nutzen.

Beispiel 3.10.5-1. String-Literale

Das Programm bestehend aus der Zusammenstellungseinheit (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

und die Zusammenstellungseinheit:

package other;
public class Other { public static String hello = "Hello"; }

erzeugt die Ausgabe:

true true true true false true

JVMS

JVMS 7 5.1 sagt :

Ein String-Literal ist eine Referenz auf eine Instanz der Klasse String und wird aus einer CONSTANT_String_info-Struktur (§4.4.3) in der binären Darstellung einer Klasse oder Schnittstelle abgeleitet. Die Struktur CONSTANT_String_info gibt die Folge von Unicode-Codepunkten an, die das Zeichenfolgenliteral bilden.

Die Java-Programmiersprache erfordert, dass identische Zeichenfolgenliterale (dh Literale, die dieselbe Folge von Codepunkten enthalten) auf dieselbe Instanz der Klasse String verweisen müssen (JLS §3.10.5). Wenn die Methode String.intern für eine beliebige Zeichenfolge aufgerufen wird, ist das Ergebnis außerdem ein Verweis auf dieselbe Klasseninstanz, die zurückgegeben würde, wenn diese Zeichenfolge als Literal angezeigt würde. Daher muss der folgende Ausdruck den Wert true haben:

("a" + "b" + "c").intern() == "abc"

Um ein Zeichenfolgenliteral abzuleiten, untersucht die Java Virtual Machine die Reihenfolge der Codepunkte, die in der Struktur CONSTANT_String_info angegeben sind.

  • Wenn die Methode String.intern zuvor für eine Instanz der Klasse String aufgerufen wurde, die eine Folge von Unicode-Codepunkten enthält, die mit der in der Struktur CONSTANT_String_info angegebenen identisch ist, ist das Ergebnis der Ableitung des String-Literal ein Verweis auf dieselbe Instanz der Klasse String.

  • Andernfalls wird eine neue Instanz der Klasse String erstellt, die die von der Struktur CONSTANT_String_info angegebene Folge von Unicode-Codepunkten enthält. Ein Verweis auf diese Klasseninstanz ist das Ergebnis der Ableitung von Zeichenfolgenliteralen. Schließlich wird die interne Methode der neuen String-Instanz aufgerufen.

Bytecode

Es ist auch lehrreich, sich die Bytecode-Implementierung in OpenJDK 7 anzusehen.

Wenn wir dekompilieren:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

Wir haben auf dem ständigen Pool:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

und main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Beachten Sie, wie:

  • 0und 3: die gleiche ldc #2Konstante wird geladen (die Literale)
  • 12: Eine neue String-Instanz wird erstellt (mit #2als Argument)
  • 35: aund cwerden als reguläre Objekte mit verglichenif_acmpne

Die Darstellung konstanter Zeichenfolgen ist auf dem Bytecode ziemlich magisch:

  • es verfügt über eine eigene CONSTANT_String_info Struktur, anders als bei normalen Objekten (zB new String)
  • Die Struktur zeigt auf eine CONSTANT_Utf8_info-Struktur , die die Daten enthält. Dies sind die einzigen erforderlichen Daten, um die Zeichenfolge darzustellen.

und das obige JVMS-Zitat scheint zu sagen, dass immer dann, wenn der Utf8, auf den gezeigt wird, derselbe ist, identische Instanzen von geladen werden ldc.

Ich habe ähnliche Tests für Felder durchgeführt und:

Fazit : Es gibt eine direkte Bytecode-Unterstützung für den Zeichenfolgenpool und die Speicherdarstellung ist effizient.

Bonus: Vergleichen Sie das mit dem Integer-Pool , der keine direkte Bytecode-Unterstützung bietet (dh kein CONSTANT_String_infoAnalogon).


1

Stellen Sie sich vor, Sie "bla"wären eine magische Fabrik wie Strings.createString("bla")(Pseudo). Die Fabrik enthält einen Pool aller Saiten, die auf diese Weise erstellt wurden.

Wenn es aufgerufen wird, prüft es, ob der Pool bereits eine Zeichenfolge mit diesem Wert enthält. Wenn true, wird dieses Zeichenfolgenobjekt zurückgegeben, sodass Zeichenfolgen, die auf diese Weise erhalten wurden, tatsächlich dasselbe Objekt sind.

Wenn nicht, erstellt es intern ein neues Zeichenfolgenobjekt, speichert es im Pool und gibt es dann zurück. Wenn also beim nächsten Mal derselbe Zeichenfolgenwert abgefragt wird, wird dieselbe Instanz zurückgegeben.

Das manuelle Erstellen new String("")überschreibt dieses Verhalten, indem der String-Literal-Pool umgangen wird. Daher sollte die Gleichheit immer überprüft werden, indem equals()die Zeichenfolge anstelle der Objektreferenzgleichheit verglichen wird.


Die 'Magic Factory', auf die Sie sich beziehen, ist nicht mehr oder weniger als der Java-Compiler. Es ist ein Fehler, diesen Prozess so zu schreiben, als ob er zur Laufzeit aufgetreten wäre.
Marquis von Lorne

1

Ein einfacher Weg, um den Unterschied zu verstehen, ist unten: -

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

Ausgabe ist

s==s1 is true
s==s2 is false

Somit erstellt new String () immer eine neue Instanz.


1

@Braj: Ich denke du hast den umgekehrten Weg erwähnt. Bitte korrigieren Sie mich, wenn ich falsch liege

Objekterstellung Zeile für Zeile:

String str1 = neuer String ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

String str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

String str3 = neuer String (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

String str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1ist nicht im Wert von Beteiligten str2oder str3oder str4in irgendeiner Art und Weise.
Marquis von Lorne

1

Jedes String-Literal wird im String-Literal-Pool erstellt und der Pool lässt keine Duplikate zu. Wenn also zwei oder mehr Zeichenfolgenobjekte mit demselben Literalwert initialisiert werden, zeigen alle Objekte auf dasselbe Literal.

String obj1 = "abc";
String obj2 = "abc";

"obj1" und "obj2" verweisen auf dasselbe Zeichenfolgenliteral, und der Zeichenfolgenliteralpool enthält nur ein "abc" -Literal.

Wenn wir ein String-Klassenobjekt mit dem neuen Schlüsselwort erstellen, wird der so erstellte String im Heapspeicher gespeichert. Jedes als Parameter an den Konstruktor der String-Klasse übergebene String-Literal wird jedoch im String-Pool gespeichert. Wenn wir mit dem neuen Operator mehrere Objekte mit demselben Wert erstellen, wird jedes Mal ein neues Objekt im Heap erstellt, da dieser neue Operator vermieden werden sollte.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" und "obj2" zeigen auf zwei verschiedene Objekte im Heap, und der String-Literal-Pool enthält nur ein "abc" -Literal.

In Bezug auf das Verhalten von Zeichenfolgen ist auch zu beachten, dass bei jeder neuen Zuweisung oder Verkettung von Zeichenfolgen ein neues Objekt im Speicher erstellt wird.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Jetzt im obigen Fall:
Zeile 1: Das Literal "abc" wird im Zeichenfolgenpool gespeichert.
Zeile 2: Das Literal "abcdef" wird im Zeichenfolgenpool gespeichert.
Zeile 3: Ein neues "xyz" -Literal wird im Zeichenfolgenpool gespeichert, und "str1" zeigt auf dieses Literal.
Zeile 4: Da der Wert durch Anhängen an eine andere Variable generiert wird, wird das Ergebnis im Heapspeicher gespeichert und das angehängte Literal "ghi" wird auf seine Existenz im Zeichenfolgenpool überprüft und erstellt, da es in nicht vorhanden ist der obige Fall.


0

Obwohl es aus Sicht eines Programmierers gleich aussieht, hat es große Auswirkungen auf die Leistung. Sie möchten fast immer das erste Formular verwenden.


0
String str = new String("hello")

Es wird geprüft, ob der String-Konstantenpool bereits den String "Hallo" enthält. Wenn vorhanden, wird kein Eintrag im String-Konstantenpool hinzugefügt. Wenn nicht vorhanden, wird ein Eintrag im String-Konstantenpool hinzugefügt.

Ein Objekt wird in einem Heapspeicherbereich erstellt und strverweist auf ein Objekt, das im Heapspeicher erstellt wurde.

Wenn Sie auf ein Punktobjekt strverweisen möchten , das im String-Konstantenpool enthalten ist, müssen Sie explizit aufrufenstr.intern();

String str = "world";

Es wird geprüft, ob der String-Konstantenpool bereits den String "Hallo" enthält. Wenn vorhanden, wird kein Eintrag im String-Konstantenpool hinzugefügt. Wenn nicht vorhanden, wird ein Eintrag im String-Konstantenpool hinzugefügt.

In beiden oben genannten Fällen strverweisen Verweise auf Zeichenfolgen, "world"die im Konstantenpool vorhanden sind.


'Es' ist der Java-Compiler. Das Zeichenfolgenliteral erstellt zur Kompilierungszeit einen eindeutigen Eintrag im Konstantenpool. Es ist ein Fehler, diesen Prozess so zu beschreiben, als ob er zur Laufzeit stattfindet.
Marquis of Lorne

Können Sie bitte klar erklären, was in diesem Beitrag falsch ist?
Jayesh

Was in diesem Beitrag falsch ist, ist, dass das String-Literal zur vollständigen Zeit gepoolt wird, wie ich bereits sagte. Nicht bei der Ausführung des Codes, wie in Ihrer Antwort.
Marquis von Lorne

@EJP Ich freue mich über Ihre Antwort. Können Sie bitte auf die genaue Zeile hinweisen, die in der Antwort falsch ist? Ich sehe, dass alle Antworten oben mit denen übereinstimmen, die ich geschrieben habe. Bitte helfen Sie, ich möchte mein Verständnis korrigieren. Vielen Dank.
Jayesh

Sie haben über den gesamten Prozess geschrieben, als ob alles stattfindet, wenn die Codezeile ausgeführt wird, was, wie ich Ihnen wiederholt gesagt habe, nicht der Fall ist. Sie können das alles nicht auf eine einzige "exakte Linie" reduzieren, die in Ihrer Antwort falsch ist.
Marquis von Lorne

0

Wenn Sie einen String als speichern

String string1 = "Hello";

Direkt erstellt JVM dann ein String-Objekt mit dem angegebenen Preis in einem separaten Speicherblock namens String-Konstantenpool.

Und wann immer wir die Tendenz haben, einen anderen String als zu produzieren

String string2 = "Hello";

JVM überprüft, ob im String-Konstantenpool ein String-Objekt mit konstantem Preis vorhanden ist, wenn dies der Fall ist, anstatt ein brandneues Objekt zu erstellen. JVM weist der neuen Variablen die Referenz des vorhandenen Objekts zu.

Und wenn wir String als speichern

String string = new String("Hello");

Mit dem neuen Schlüsselwort wird ein brandneues Objekt mit dem angegebenen Preis erstellt, unabhängig vom Inhalt des String-Konstantenpools.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.