Quando dichiari una variabile String
(che è immutabile ) come final
e la inizializzi con un'espressione costante in fase di compilazione, diventa anche un'espressione costante in fase di compilazione e il suo valore viene sottolineato dal compilatore in cui viene utilizzato. Quindi, nel tuo secondo esempio di codice, dopo aver incorporato i valori, la concatenazione di stringhe viene tradotta dal compilatore in:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
che se confrontato con "string"
ti darà true
, perché i valori letterali delle stringhe sono internati .
Da JLS §4.12.4 - final
Variabili :
Una variabile di tipo o tipo primitivo String
, che è final
e inizializzata con un'espressione costante in fase di compilazione (§15.28), è chiamata variabile costante .
Anche da JLS § 15.28 - Espressione costante:
Costanti in fase di compilazione espressioni del tipo String
sono sempre "internati" , in modo da condividere le istanze univoche, utilizzando il metodo String#intern()
.
Questo non è il caso nel tuo primo esempio di codice, dove le String
variabili non lo sono final
. Quindi, non sono espressioni costanti in fase di compilazione. L'operazione di concatenazione verrà ritardata fino al runtime, portando così alla creazione di un nuovo String
oggetto. È possibile verificarlo confrontando il codice byte di entrambi i codici.
Il primo esempio di codice (non final
versione) viene compilato nel seguente codice byte:
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
Chiaramente sta memorizzando str
e ing
in due variabili separate, e usando StringBuilder
per eseguire l'operazione di concatenazione.
Considerando che il tuo secondo esempio di codice ( final
versione) è simile al seguente:
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
Quindi incorpora direttamente la variabile finale per creare String string
al momento della compilazione, che viene caricata ldc
dall'operazione nel passaggio 0
. Quindi la seconda stringa letterale viene caricata ldc
dall'operazione nel passaggio 7
. Non comporta la creazione di alcun nuovo String
oggetto in fase di esecuzione. Le String sono già note al momento della compilazione e vengono internate.