Cuando declara una variable String
(que es inmutable ) como final
, y la inicializa con una expresión constante en tiempo de compilación, también se convierte en una expresión constante en tiempo de compilación, y el compilador en el que se utiliza inserta su valor. Entonces, en su segundo ejemplo de código, después de incluir los valores, el compilador traduce la concatenación de cadenas a:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
que en comparación con "string"
te dará true
, porque los literales de cadena están internados .
De JLS §4.12.4 - final
Variables :
Una variable de tipo o tipo primitivo String
, que se final
inicializa con una expresión constante en tiempo de compilación (§15.28), se denomina variable constante .
También de JLS §15.28 - Expresión constante:
Las expresiones de tipo constantes en tiempo de compilación String
siempre están "internados" para compartir instancias únicas, utilizando el método String#intern()
.
Este no es el caso en su primer ejemplo de código, donde las String
variables no lo son final
. Por lo tanto, no son expresiones constantes en tiempo de compilación. La operación de concatenación allí se retrasará hasta el tiempo de ejecución, lo que conducirá a la creación de un nuevo String
objeto. Puede verificar esto comparando el código de bytes de ambos códigos.
El primer ejemplo de código (no final
versión) se compila con el siguiente código de bytes:
Code:
0: ldc #2; //String str
2: astore_1
3: ldc #3; //String ing
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_3
29: ldc #9; //String string
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #10; //Method java/io/PrintStream.println:(Z)V
42: return
Claramente está almacenando str
y ing
en dos variables separadas, y está utilizando StringBuilder
para realizar la operación de concatenación.
Mientras que su segundo ejemplo de código ( final
versión) se ve así:
Code:
0: ldc #2; //String string
2: astore_3
3: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_3
7: ldc #2; //String string
9: if_acmpne 16
12: iconst_1
13: goto 17
16: iconst_0
17: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
20: return
Por lo tanto, alinea directamente la variable final para crear String string
en tiempo de compilación, que se carga por ldc
operación en el paso 0
. Luego, el segundo literal de cadena se carga por ldc
operación en el paso 7
. No implica la creación de ningún String
objeto nuevo en tiempo de ejecución. La cadena ya se conoce en tiempo de compilación, y están internadas.