O método "antigo" produz um monte de StringBuilder
operações orientadas. Considere este programa:
public class Example {
public static void main(String[] args)
{
String result = args[0] + "-" + args[1] + "-" + args[2];
System.out.println(result);
}
}
Se compilarmos isso com JDK 8 ou anterior e, em seguida, usarmos javap -c Example
para ver o bytecode, veremos algo assim:
public class Example {
public Exemplo ();
Código:
0: aload_0
1: invokespecial # 1 // Método java / lang / Object. "<init>" :() V
4: retorno
public static void main (java.lang.String []);
Código:
0: novo # 2 // classe java / lang / StringBuilder
3: dup
4: invokespecial # 3 // Método java / lang / StringBuilder. "<init>" :() V
7: aload_0
8: iconst_0
9: aaload
10: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
13: ldc # 5 // String -
15: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
18: aload_0
19: iconst_1
20: aaload
21: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
24: ldc # 5 // String -
26: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
29: aload_0
30: iconst_2
31: aaload
32: invokevirtual # 4 // Método java / lang / StringBuilder.append: (Ljava / lang / String;) Ljava / lang / StringBuilder;
35: invokevirtual # 6 // Método java / lang / StringBuilder.toString :() Ljava / lang / String;
38: astore_1
39: getstatic # 7 // Campo java / lang / System.out: Ljava / io / PrintStream;
42: aload_1
43: invokevirtual # 8 // Método java / io / PrintStream.println: (Ljava / lang / String;) V
46: retorno
}
Como você pode ver, ele cria um StringBuilder
e usa append
. Isso é bastante ineficiente, pois a capacidade padrão do buffer embutido StringBuilder
é de apenas 16 caracteres e não há como o compilador saber alocar mais com antecedência, então ele acaba tendo que realocar. É também um monte de chamadas de método. (Observe que a JVM às vezes pode detectar e reescrever esses padrões de chamadas para torná-los mais eficientes.)
Vejamos o que o Java 9 gera:
public class Example {
public Exemplo ();
Código:
0: aload_0
1: invokespecial # 1 // Método java / lang / Object. "<init>" :() V
4: retorno
public static void main (java.lang.String []);
Código:
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 // Campo java / lang / System.out: Ljava / io / PrintStream;
18: aload_1
19: invokevirtual # 4 // Método java / io / PrintStream.println: (Ljava / lang / String;) V
22: retorno
}
Oh meu, mas isso é mais curto. :-) Ele faz uma única chamada para makeConcatWithConstants
from StringConcatFactory
, que diz isso em seu Javadoc:
Métodos para facilitar a criação de métodos de concatenação de String, que podem ser usados para concatenar eficientemente um número conhecido de argumentos de tipos conhecidos, possivelmente após adaptação de tipo e avaliação parcial de argumentos. Esses métodos são normalmente usados como métodos de bootstrap para invokedynamic
sites de chamada, para oferecer suporte ao recurso de concatenação de string da linguagem de programação Java.