Ich bin alle verwirrt, nachdem ich den Artikel auf der Javaranch-Website von Corey McGlone, dem Autor von The SCJP Tip Line, gelesen habe. genannt Strings, Literally und der SCJP Java 6 Programmer Guide von Kathy Sierra (Mitbegründerin von javaranch) und Bert Bates.
Ich werde versuchen zu zitieren, was Herr Corey und Frau Kathy Sierra über String Literal Pool zitiert haben.
1. Laut Corey McGlone:
String Literal Pool ist eine Sammlung von Referenzen, die auf die String-Objekte verweisen.
String s = "Hello";
(Angenommen, auf dem Heap befindet sich kein Objekt mit dem Namen "Hallo"), erstellt ein String-Objekt"Hello"
auf dem Heap und platziert einen Verweis auf dieses Objekt im String-Literal-Pool (Konstantentabelle).String a = new String("Bye");
(Angenommen, auf dem Heap befindet sich kein Objekt mit dem Namen "Bye". Dernew
Operator verpflichtet die JVM, ein Objekt auf dem Heap zu erstellen.
Jetzt ist die Erklärung des "new"
Operators für die Erstellung eines Strings und seiner Referenz in diesem Artikel etwas verwirrend, daher setze ich den Code und die Erklärung aus dem Artikel selbst so ein, wie er unten ist.
public class ImmutableStrings
{
public static void main(String[] args)
{
String one = "someString";
String two = new String("someString");
System.out.println(one.equals(two));
System.out.println(one == two);
}
}
In diesem Fall haben wir aufgrund des Schlüsselworts tatsächlich ein etwas anderes Verhalten. "new."
In einem solchen Fall werden Verweise auf die beiden String-Literale immer noch in die Konstantentabelle (den String-Literal-Pool) eingefügt, aber wenn Sie zum Schlüsselwort kommen "new,"
Die JVM muss zur Laufzeit ein neues String-Objekt erstellen, anstatt das aus der Konstantentabelle zu verwenden.
Hier ist das Diagramm, das es erklärt.
Bedeutet das also, dass auch der String Literal Pool einen Verweis auf dieses Objekt hat?
Hier ist der Link zum Artikel von Corey McGlone
http://www.javaranch.com/journal/200409/Journal200409.jsp#a1
2. Laut Kathy Sierra und Bert Bates im SCJP-Buch:
Um Java speichereffizienter zu machen, hat die JVM einen speziellen Speicherbereich namens "String-Konstantenpool" reserviert. Wenn der Compiler auf ein String-Literal stößt, überprüft er den Pool, um festzustellen, ob bereits ein identischer String vorhanden ist oder nicht. Wenn nicht, wird ein neues String-Literal-Objekt erstellt.
String s = "abc";
// Erstellt ein String-Objekt und eine Referenzvariable ....das ist in Ordnung, aber dann war ich durch diese Aussage verwirrt:
String s = new String("abc")
// Erstellt zwei Objekte und eine Referenzvariable.In dem Buch heißt es, dass ... ein neues String-Objekt im normalen Speicher (ohne Pool) und "s" darauf verweisen ... während ein zusätzliches Literal "abc" in den Pool gestellt wird.
Die obigen Zeilen im Buch kollidieren mit denen im Artikel von Corey McGlone.
Wenn der String-Literal-Pool eine Sammlung von Verweisen auf das von Corey McGlone erwähnte String-Objekt ist, warum wird dann das Literal-Objekt "abc" in den Pool gestellt (wie im Buch erwähnt)?
Und wo befindet sich dieser String Literal Pool?
Bitte klären Sie diesen Zweifel, obwohl es beim Schreiben eines Codes nicht allzu wichtig ist, aber unter dem Gesichtspunkt der Speicherverwaltung sehr wichtig ist, und das ist der Grund, warum ich diese Funda löschen möchte.
quelle
Antworten:
Ich denke, der wichtigste Punkt, den Sie hier verstehen sollten, ist die Unterscheidung zwischen
String
Java-Objekt und seinem Inhalt -char[]
im privatenvalue
Bereich .String
ist im Grunde ein Wrapper um einchar[]
Array, der es einkapselt und es unmöglich macht, es zu ändern, damit dasString
unveränderlich bleibt. DieString
Klasse merkt sich auch, welche Teile dieses Arrays tatsächlich verwendet werden (siehe unten). Dies alles bedeutet, dass Sie zwei verschiedeneString
Objekte (ziemlich leicht) haben können, die auf dasselbe zeigenchar[]
.Ich werde Ihnen einige Beispiele zeigen, zusammen mit
hashCode()
jedemString
undhashCode()
internemchar[] value
Bereich (ich es nenne Text aus Zeichenfolge zu unterscheiden). Zum Schluss zeige ich diejavap -c -verbose
Ausgabe zusammen mit dem konstanten Pool für meine Testklasse. Bitte verwechseln Sie den Klassenkonstantenpool nicht mit dem String-Literal-Pool. Sie sind nicht ganz gleich. Siehe auch Grundlegendes zur Ausgabe von javap für den konstanten Pool .Voraussetzungen
Zum Testen habe ich eine solche Dienstprogrammmethode erstellt, die die
String
Kapselung unterbricht:private int showInternalCharArrayHashCode(String s) { final Field value = String.class.getDeclaredField("value"); value.setAccessible(true); return value.get(s).hashCode(); }
Es wird Druck
hashCode()
vonchar[] value
effektiv helfen uns , ob diese besondere verstehenString
Punkte auf den gleichenchar[]
Text oder nicht.Zwei String-Literale in einer Klasse
Beginnen wir mit dem einfachsten Beispiel.
Java-Code
String one = "abc"; String two = "abc";
Übrigens, wenn Sie einfach schreiben
"ab" + "c"
, führt der Java-Compiler zur Kompilierungszeit eine Verkettung durch und der generierte Code ist genau der gleiche. Dies funktioniert nur, wenn alle Zeichenfolgen zur Kompilierungszeit bekannt sind.Klasse konstanter Pool
Jede Klasse verfügt über einen eigenen Konstantenpool - eine Liste von Konstantenwerten, die wiederverwendet werden können, wenn sie im Quellcode mehrmals vorkommen. Es enthält allgemeine Zeichenfolgen, Zahlen, Methodennamen usw.
Hier sind die Inhalte des konstanten Pools in unserem obigen Beispiel.
const #2 = String #38; // abc //... const #38 = Asciz abc;
Das Wichtigste ist die Unterscheidung zwischen
String
konstantem Objekt (#2
) und Unicode-codiertem Text"abc"
(#38
), auf den die Zeichenfolge zeigt.Bytecode
Hier wird Bytecode generiert. Beachten Sie, dass sowohl
one
als auchtwo
Referenzen mit derselben#2
Konstante zugewiesen werden, die auf"abc"
string zeigt:ldc #2; //String abc astore_1 //one ldc #2; //String abc astore_2 //two
Ausgabe
Für jedes Beispiel drucke ich die folgenden Werte:
Kein Wunder, dass beide Paare gleich sind:
23583040 23583040 8918249 8918249
Dies bedeutet, dass nicht nur beide Objekte auf dasselbe zeigen
char[]
(derselbe Text darunter), sodass derequals()
Test bestanden wird. Aber noch mehrone
undtwo
sind genau die gleichen Referenzen! Soone == two
ist es auch. Offensichtlich wennone
undtwo
auf dasselbe Objekt zeigen, dannone.value
undtwo.value
muss gleich sein.Wörtlich und
new String()
Java-Code
Nun das Beispiel, auf das wir alle gewartet haben - ein String-Literal und ein neues
String
mit demselben Literal. Wie wird das funktionieren?String one = "abc"; String two = new String("abc");
Die Tatsache, dass die
"abc"
Konstante im Quellcode zweimal verwendet wird, sollte Ihnen einen Hinweis geben ...Klasse konstanter Pool
Das gleiche wie oben.
Bytecode
ldc #2; //String abc astore_1 //one new #3; //class java/lang/String dup ldc #2; //String abc invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V astore_2 //two
Schau genau hin! Das erste Objekt wird auf die gleiche Weise wie oben erstellt, keine Überraschung. Es wird nur ein konstanter Verweis auf bereits erstellt
String
(#2
) aus dem konstanten Pool verwendet. Das zweite Objekt wird jedoch über einen normalen Konstruktoraufruf erstellt. Aber! Der ersteString
wird als Argument übergeben. Dies kann dekompiliert werden zu:String two = new String(one);
Ausgabe
Die Ausgabe ist etwas überraschend. Das zweite Paar, das Verweise auf
String
Objekte darstellt, ist verständlich - wir haben zweiString
Objekte erstellt - eines wurde für uns im konstanten Pool erstellt und das zweite wurde manuell für erstellttwo
. Aber warum schlägt das erste Paar auf der Erde vor, dass beideString
Objekte auf dasselbechar[] value
Array zeigen?!41771 41771 8388097 16585653
Wenn Sie sich die Funktionsweise des
String(String)
Konstruktors ansehen (hier stark vereinfacht), wird dies deutlich :public String(String original) { this.offset = original.offset; this.count = original.count; this.value = original.value; }
Sehen? Wenn Sie ein neues
String
Objekt basierend auf einem vorhandenen erstellen , wird es wiederverwendetchar[] value
.String
s unveränderlich sind, muss keine Datenstruktur kopiert werden, von der bekannt ist, dass sie niemals geändert wird.Ich denke, dies ist der Hinweis auf Ihr Problem: Selbst wenn Sie zwei
String
Objekte haben, können diese dennoch auf denselben Inhalt verweisen. Und wie Sie sehen können, ist dasString
Objekt selbst ziemlich klein.Laufzeitänderung und
intern()
Java-Code
Angenommen, Sie haben anfangs zwei verschiedene Zeichenfolgen verwendet, aber nach einigen Änderungen sind alle gleich:
String one = "abc"; String two = "?abc".substring(1); //also two = "abc"
Der Java-Compiler (zumindest meiner) ist nicht clever genug, um eine solche Operation zur Kompilierungszeit auszuführen.
Klasse konstanter Pool
Plötzlich hatten wir zwei konstante Zeichenfolgen, die auf zwei verschiedene konstante Texte zeigten:
const #2 = String #44; // abc const #3 = String #45; // ?abc const #44 = Asciz abc; const #45 = Asciz ?abc;
Bytecode
ldc #2; //String abc astore_1 //one ldc #3; //String ?abc iconst_1 invokevirtual #4; //Method String.substring:(I)Ljava/lang/String; astore_2 //two
Die Faustschnur ist wie gewohnt aufgebaut. Die zweite wird erstellt, indem zuerst die konstante
"?abc"
Zeichenfolge geladen und dann aufgerufensubstring(1)
wird.Ausgabe
Kein Wunder hier - wir haben zwei verschiedene Zeichenfolgen, die auf zwei verschiedene
char[]
Texte im Speicher verweisen :27379847 7615385 8388097 16585653
Nun, die Texte sind nicht wirklich unterschiedlich , die
equals()
Methode wird immer noch nachgebentrue
. Wir haben zwei unnötige Kopien desselben Textes.Jetzt sollten wir zwei Übungen machen. Versuchen Sie zunächst:
vor dem Drucken von Hash-Codes. Nicht nur beide
one
undtwo
zeigen auf den gleichen Text, sondern sie sind die gleiche Referenz!11108810 11108810 15184449 15184449
Das bedeutet , beide
one.equals(two)
undone == two
Tests wird vorübergehen. Außerdem haben wir etwas Speicherplatz gespart, da"abc"
Text nur einmal im Speicher angezeigt wird (die zweite Kopie wird durch Müll gesammelt).Die zweite Übung ist etwas anders. Schauen Sie sich Folgendes an:
String one = "abc"; String two = "abc".substring(1);
Offensichtlich
one
undtwo
sind zwei verschiedene Objekte, die auf zwei verschiedene Texte verweisen. Aber wie kommt es, dass die Ausgabe darauf hindeutet, dass beide auf dasselbechar[]
Array verweisen ?!?23583040 23583040 11108810 8918249
Ich werde die Antwort dir überlassen. Sie erfahren, wie es
substring()
funktioniert, welche Vorteile ein solcher Ansatz bietet und wann er zu großen Problemen führen kann .quelle