Ich wurde in einem Interview nach der Anzahl der Objekte gefragt, die für das gegebene Problem erstellt werden:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
Ich antwortete, dass 6 Objekte im String-Pool erstellt werden würde.
3 wäre für jede der drei Variablen.
1 wäre fürstr1 + str2
(sagen wirstr
).
1 wäre fürstr2 + str3
.
1 wäre für diestr + str3
(str = str1 + str2
).
Ist die Antwort, die ich gegeben habe, richtig? Wenn nicht, wie lautet die richtige Antwort?
Jede Antwort auf Ihre Frage hängt von der JVM-Implementierung und der aktuell verwendeten Version Java ab. Ich denke, es ist eine unvernünftige Frage, die man in einem Interview stellt.
Auf meinem Computer führt Ihr Snippet mit Java 1.8.0_201 zu diesem Bytecode
L0
LINENUMBER 13 L0
LDC "First"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "Second"
ASTORE 2
L2
LINENUMBER 15 L2
LDC "Third"
ASTORE 3
L3
LINENUMBER 16 L3
NEW Java/lang/StringBuilder
DUP
INVOKESPECIAL Java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL Java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
dies beweist, dass 5 Objekte erstellt werden (3 String
Literale *, 1 StringBuilder
=, 1 dynamisch erzeugte String
Instanz von StringBuilder#toString
).
Auf meinem Computer lautet der Bytecode mit Java 12.0.2
// identical to the bytecode above
L3
LINENUMBER 16 L3
ALOAD 1
ALOAD 2
ALOAD 3
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
Java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001\u0001"
]
ASTORE 4
welches magisch "die richtige Antwort" in 4 Objekte ändert, da kein Zwischenprodukt StringBuilder
beteiligt ist.
* Lass uns etwas tiefer graben.
12.5. Erstellung neuer Klasseninstanzen
In den folgenden Situationen kann implizit eine neue Klasseninstanz erstellt werden:
- Durch das Laden einer Klasse oder Schnittstelle, die ein Zeichenfolgenliteral enthält ( §3.10.5 ), wird möglicherweise ein neues Zeichenfolgenobjekt erstellt, das das Literal darstellt. (Dies tritt nicht auf, wenn zuvor eine Zeichenfolge interniert wurde, die dieselbe Folge von Unicode-Codepunkten angibt.)
Mit anderen Worten, wenn Sie eine Anwendung starten, befinden sich bereits Objekte im Zeichenfolgenpool. Sie wissen kaum, was sie sind und woher sie kommen (es sei denn, Sie scannen alle geladenen Klassen nach allen darin enthaltenen Literalen).
Die Klasse Java.lang.String
Wird zweifellos als wesentliche JVM-Klasse geladen, dh alle ihre Literale werden erstellt und in den Pool gestellt.
Nehmen wir ein zufällig ausgewähltes Snippet aus dem Quellcode von String
, wählen Sie ein paar Literale aus, setzen Sie einen Haltepunkt ganz am Anfang unseres Programms und prüfen Sie, ob der Pool diese Literale enthält.
public final class String
implements Java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
public String repeat(int count) {
// ...
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
}
...
}
Sie sind tatsächlich da.
Als interessanter Fund hat die Filterung dieser IDEA einen Nebeneffekt: Die von mir gesuchten Teilzeichenfolgen wurden ebenfalls dem Pool hinzugefügt. Die Poolgröße wurde um eins erhöht ("bytes String"
Wurde hinzugefügt), nachdem ich this.contains("bytes String")
angewendet habe.
Wo bleibt uns das?
Wir haben keine Ahnung, ob "First"
Vor dem Aufruf von String str1 = "First";
Erstellt und interniert wurde, daher können wir nicht sicher sagen, dass die Zeile eine neue Instanz erstellt.
Mit den gegebenen Informationen kann die Frage nicht definitiv beantwortet werden. Wie in der JLS angegeben, §15.18.1 :
... Um die Leistung der wiederholten Verkettung von Zeichenfolgen zu erhöhen, kann ein Java -Compiler die Klasse
StringBuffer
oder eine ähnliche Technik verwenden, um die Anzahl der von erstellten Zwischenzeichenfolgenobjekte zu verringern Auswertung eines Ausdrucks.
Dies bedeutet, dass die Antwort zumindest vom konkreten Java -Compiler) abhängt.
Ich denke, das Beste, was wir tun können, ist, ein Intervall als Antwort zu geben:
str1
bis str3
niemals verwendet werden, und die Verkettung während der Kompilierung falten, sodass nur ein String
- Objekt erstellt wird (dasjenige, auf das verwiesen wird) von str4
)String
s sollte 5 betragen: jeweils eine für str1
Bis str3
, Eine für tmp = str1 + str2
Und eine für str4 = tmp + str3
.Also ... meine Antwort wäre "etwas zwischen einem und fünf String
- Objekten". Was die Gesamtzahl der Objekte betrifft, die nur für diesen Vorgang erstellt wurden ... weiß ich nicht. Dies kann auch davon abhängen, wie genau z. StringBuffer
ist implementiert.
Nebenbei: Ich frage mich, warum solche Fragen gestellt wurden. Normalerweise muss man sich nicht um diese Details kümmern.
Java 8 wird wahrscheinlich 5 Objekte erstellen:
StringBuilder
String
Mit Java 9 Dinge geändert == und String
Verkettung verwendet StringBuilder
nicht mehr.
Es sollte 5 sein:
drei für die drei Literale (zugewiesen an str1
, str2
und str3
)
eins für str1 + str2
eins für (result from the previous operation) + str3
(Zugewiesen an str4
)
Eine konforme Java -Implementierung kann die Zeichenfolgen zur Laufzeit oder zur Kompilierungszeit auf beliebig viele Arten verketten und benötigt eine beliebige Anzahl von Laufzeitobjekten, einschließlich Nullobjekten, wenn festgestellt wird, dass das Ergebnis nicht vorliegt zur Laufzeit benötigt.
Im String-Konstantenpool werden 4 String-Objekte erstellt. 3 für Literale und 1 für Verkettung.
wenn wir verwenden
String s1 = new String("one")
es werden zwei Objekte erstellt, eines im konstanten Pool und eines im Heapspeicher.
wenn wir definieren:
String s1 = "one";
String s2 = new String("one");
es werden zwei Objekte erstellt, eines im konstanten Pool und eines im Heapspeicher.
Durch die Verkettungsoperation werden nicht so viele String-Objekte erstellt. Es erstellt ein StringBuilder
und hängt dann die Zeichenfolgen an. Es kann also 5 Objekte geben, 3 (Variablen) + 1 (sb) + 1 (verkettete Zeichenfolge).