Quando você declara uma variável String
(que é imutável ) como final
e 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 - final
Variáveis :
Uma variável do tipo ou tipo primitivo String
, que é final
inicializada 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 String
sã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 String
variá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 String
objeto. Você pode verificar isso comparando o código de bytes dos dois códigos.
O primeiro exemplo de código (não final
versã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 str
e ing
em duas variáveis separadas e usando StringBuilder
para executar a operação de concatenação.
Visto que o seu segundo exemplo de código ( final
versã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 string
no tempo de compilação, que é carregado pela ldc
operação na etapa 0
. Em seguida, a segunda string literal é carregada pela ldc
operação na etapa 7
. Não envolve a criação de nenhum novo String
objeto em tempo de execução. A String já é conhecida no momento da compilação e elas são internadas.