¿Cómo se realiza la concatenación de cadenas finales en Java?
string (1)
Cuando compilo este fragmento.
public class InternTest {
public static void main(String...strings ){
final String str1="str";
final String str2="ing";
String str= str1+str2;
}
}
Que produce el siguiente código de byte
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=1, locals=4, args_size=1
0: ldc #16 // String str
2: astore_1
3: ldc #18 // String ing
5: astore_2
6: ldc #20 // String string
8: astore_3
9: return
por lo tanto, la cadena "cadena" literal ya está allí en el conjunto constante que se inserta 6: ldc #20 // String string
en la pila en esta línea.
Cotizando JSL
De JLS §4.12.4 - Variables finales:
Una variable de tipo primitivo o tipo String, que es final e inicializa con una expresión de constante de compilación (§15.28), se denomina variable constante.
También de JLS §15.28 - ConstantExpression:
Las expresiones constantes en tiempo de compilación de tipo Cadena siempre se "internan" para compartir instancias únicas, utilizando el método Cadena # intern ().
Así que sé que str1 y str2 se internarán en el momento en que se haya creado. "Str" y "ing" compartirán la misma memoria en la línea String str= str1+str2;
Pero cómo str1 + str2 produce directamente "cadena" en el conjunto de cadenas constantes. Sin invocar ninguna clase de generador de cadenas como lo hace cuando no escribo final
. ? Para ver si tiene algo que ver con cosas internas.
Escribí este fragmento
public class IntermTest {
public static void main(String...strings ){
String str1=("str").intern();
String str2=("ing").intern();
String str= str1+str2;
}
}
Pero cuando generé el código byte obtuve este
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=3, locals=4, args_size=1
0: ldc #16 // String str
2: invokevirtual #18 // Method java/lang/String.intern:
()Ljava/lang/String;
5: astore_1
6: ldc #24 // String ing
8: invokevirtual #18 // Method java/lang/String.intern:
()Ljava/lang/String;
11: astore_2
12: new #26 // class java/lang/StringBuilder
15: dup
16: aload_1
17: invokestatic #28 // Method java/lang/String.valueOf
:(Ljava/lang/Object;)Ljava/lang/String;
20: invokespecial #32 // Method java/lang/StringBuilder.
"<init>":(Ljava/lang/String;)V
23: aload_2
24: invokevirtual #35 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #39 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
30: astore_3
31: return
De hecho, también utiliza stringBuilder
para la concatenación. Así que tiene que hacer algo con final. ¿Hay algo especial en las cuerdas final
que definitivamente no conozco?
http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28 dice que
Los nombres simples (§6.5.6.1) que se refieren a variables constantes (§4.12.4) son expresiones constantes.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28 también dice:
Una expresión constante es una expresión que denota un valor de tipo primitivo o una cadena que no se completa de forma abrupta y se compone utilizando solo lo siguiente:
- Literales de tipo primitivo y literales de tipo String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
- [...]
- Los operadores aditivos + y - (§15.18)
- [...]
- Nombres simples (§6.5.6.1) que se refieren a variables constantes (§4.12.4).
Ejemplo 15.28-1. Expresiones constantes
[...]
"El entero" + Long.MAX_VALUE + "es muy grande".
Dado que esas dos variables son expresiones constantes, el compilador realiza la concatenación:
String str = str1 + str2;
se compila de la misma manera que
String str = "str" + "ing";
que se compila de la misma manera que
String str = "string";