Quando você declara uma variável String(que é imutável ) como finale a inicializa com uma expressão constante em tempo de compilação, ela também se torna uma expressão constante em tempo de compilação e seu valor é incorporado pelo compilador em que é usado. Portanto, no seu segundo exemplo de código, após incluir os valores, a concatenação de strings é convertida pelo compilador em:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
que, quando comparado a "string", fornece a você true, porque os literais de string são internados .
Do JLS §4.12.4 - finalVariáveis :
Uma variável do tipo ou tipo primitivo String, que é finalinicializada com uma expressão constante em tempo de compilação (§15.28), é chamada de variável constante .
Também de JLS §15.28 - Expressão constante:
Constantes expressões de compilação em tempo de tipo Stringsão sempre "internado" , de modo a compartilhar instâncias exclusivas, utilizando o método String#intern().
Este não é o caso no seu primeiro exemplo de código, onde as Stringvariáveis não são final. Portanto, eles não são expressões constantes em tempo de compilação. A operação de concatenação será adiada até o tempo de execução, levando à criação de um novo Stringobjeto. Você pode verificar isso comparando o código de bytes dos dois códigos.
O primeiro exemplo de código (não finalversão) é compilado no seguinte 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, ele está armazenando stre ingem duas variáveis separadas e usando StringBuilderpara executar a operação de concatenação.
Visto que o seu segundo exemplo de código ( finalversão) se parece com o seguinte:
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 isso, alinha diretamente a variável final para criar String stringno tempo de compilação, que é carregado pela ldcoperação na etapa 0. Em seguida, a segunda string literal é carregada pela ldcoperação na etapa 7. Não envolve a criação de nenhum novo Stringobjeto em tempo de execução. A String já é conhecida no momento da compilação e elas são internadas.