Der "alte" Weg gibt eine Reihe von StringBuilder
orientierten Operationen aus. Betrachten Sie dieses Programm:
public class Example {
public static void main(String[] args)
{
String result = args[0] + "-" + args[1] + "-" + args[2];
System.out.println(result);
}
}
Wenn wir das mit JDK 8 oder früher kompilieren und dann javap -c Example
den Bytecode verwenden, sehen wir ungefähr Folgendes:
öffentliche Klasse Beispiel {
öffentliches Beispiel ();
Code:
0: aload_0
1: invokespecial # 1 // Methode java / lang / Object. "<Init>" :() V.
4: zurück
public static void main (java.lang.String []);
Code:
0: neue # 2 // Klasse java / lang / StringBuilder
3: dup
4: invokespecial # 3 // Methode java / lang / StringBuilder. "<Init>" :() V.
7: aload_0
8: iconst_0
9: aaload
10: invokevirtual # 4 // Methode java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
13: ldc # 5 // String -
15: invokevirtual # 4 // Methode java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
18: aload_0
19: iconst_1
20: Aaload
21: invokevirtual # 4 // Methode java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
24: ldc # 5 // String -
26: invokevirtual # 4 // Methode java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
29: aload_0
30: iconst_2
31: Aaload
32: invokevirtual # 4 // Methode java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
35: invokevirtual # 6 // Methode java / lang / StringBuilder.toString :() Ljava / lang / String;
38: astore_1
39: getstatic # 7 // Feld java / lang / System.out: Ljava / io / PrintStream;
42: aload_1
43: invokevirtual # 8 // Methode java / io / PrintStream.println: (Ljava / lang / String;) V.
46: zurück
}}
Wie Sie sehen können, erstellt es ein StringBuilder
und verwendet append
. Dies ist bekanntermaßen ziemlich ineffizient, da die Standardkapazität des integrierten Puffers StringBuilder
nur 16 Zeichen beträgt und der Compiler nicht wissen kann, ob er im Voraus mehr zuweisen muss, sodass er letztendlich neu zuweisen muss. Es ist auch eine Reihe von Methodenaufrufen. (Beachten Sie, dass die JVM diese Anrufmuster manchmal erkennen und neu schreiben kann, um sie jedoch effizienter zu gestalten.)
Schauen wir uns an, was Java 9 generiert:
öffentliche Klasse Beispiel {
öffentliches Beispiel ();
Code:
0: aload_0
1: invokespecial # 1 // Methode java / lang / Object. "<Init>" :() V.
4: zurück
public static void main (java.lang.String []);
Code:
0: aload_0
1: iconst_0
2: Aaload
3: aload_0
4: iconst_1
5: Aaload
6: aload_0
7: iconst_2
8: Aaload
9: invokedynamic # 2, 0 // InvokeDynamic # 0: makeConcatWithConstants: (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String;) Ljava / lang / String;
14: astore_1
15: getstatic # 3 // Feld java / lang / System.out: Ljava / io / PrintStream;
18: aload_1
19: invokevirtual # 4 // Methode java / io / PrintStream.println: (Ljava / lang / String;) V.
22: zurück
}}
Oh mein Gott, aber das ist kürzer. :-) Es macht einen einzigen Anruf makeConcatWithConstants
von StringConcatFactory
, was dies in seinem Javadoc sagt:
Methoden zur Erleichterung der Erstellung von String-Verkettungsmethoden, mit denen eine bekannte Anzahl von Argumenten bekannter Typen effizient verkettet werden kann, möglicherweise nach Typanpassung und teilweiser Auswertung von Argumenten. Diese Methoden werden normalerweise als Bootstrap-Methoden für invokedynamic
Aufrufstandorte verwendet, um die Funktion zur Verkettung von Zeichenfolgen in der Java-Programmiersprache zu unterstützen.