Lorsque vous déclarez un String
(qui est immuable ) variable comme final
et l'initialiser avec une expression constante du temps de compilation, elle devient également une expression constante du temps de compilation, et sa valeur est inlined par le compilateur où elle est utilisée. Ainsi, dans votre deuxième exemple de code, après l'inlining des valeurs, la concaténation de chaînes de caractères est traduite par le compilateur en :
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
qui, comparé à "string"
vous donnera true
parce que les chaînes de caractères sont interné .
Desde JLS §4.12.4 - final
Variables :
Une variable de type primitif ou de type String
c'est-à-dire final
et initialisée avec une expression constante en temps de compilation (§15.28), est appelée une variable constante .
Également de JLS §15.28 - Expression constante :
Les expressions constantes du temps de compilation de type String
sont toujours "interné" de manière à partager des instances uniques, en utilisant la méthode String#intern()
.
Ce n'est pas le cas dans votre premier exemple de code, où l'élément String
les variables ne sont pas final
. Il ne s'agit donc pas d'expressions constantes au moment de la compilation. L'opération de concaténation y sera retardée jusqu'au moment de l'exécution, ce qui conduira à la création d'un nouveau fichier String
objet. Vous pouvez le vérifier en comparant le code d'octet des deux codes.
Le premier exemple de code (non final
version) est compilé dans le code d'octet suivant :
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
Il est clair qu'il stocke str
y ing
dans deux variables distinctes, et en utilisant StringBuilder
pour effectuer l'opération de concaténation.
Alors que, votre deuxième exemple de code ( final
version) ressemble à ça :
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
Donc il met directement en ligne la variable finale pour créer String string
au moment de la compilation, qui est chargé par ldc
opération en étape 0
. Ensuite, la deuxième chaîne littérale est chargée par ldc
opération en étape 7
. Il n'implique pas la création d'un nouveau String
au moment de l'exécution. La chaîne est déjà connue au moment de la compilation, et ils sont internés.