Lorsque vous déclarez une variable String
(qui est immuable ) en tant que final
et l'initialisez avec une expression constante au moment de la compilation, elle devient également une expression constante au moment de la compilation, et sa valeur est indiquée par le compilateur où elle est utilisée. Ainsi, dans votre deuxième exemple de code, après avoir inséré les valeurs, la concaténation de chaîne est traduite par le compilateur en:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
qui, comparé à "string"
, vous donnera true
, car les littéraux de chaîne sont internés .
De JLS §4.12.4 - final
Variables :
Une variable de type ou type primitif String
, c'est-à-dire final
initialisée avec une expression constante au moment de la compilation (§15.28), est appelée variable constante .
Aussi de JLS §15.28 - Expression constante:
Les expressions constantes de type à la compilation String
sont toujours "internées" afin de partager des instances uniques, à l'aide de la méthode String#intern()
.
Ce n'est pas le cas dans votre premier exemple de code, où les String
variables ne le sont pas final
. Ce ne sont donc pas des expressions constantes à la compilation. L'opération de concaténation y sera retardée jusqu'à l'exécution, conduisant ainsi à la création d'un nouvel String
objet. Vous pouvez le vérifier en comparant le code 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
De toute évidence, il stocke str
et ing
dans deux variables distinctes et utilise StringBuilder
pour effectuer l'opération de concaténation.
Alors que votre deuxième exemple de code ( final
version) ressemble à ceci:
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
Ainsi, il insère directement la variable finale pour créer une chaîne string
au moment de la compilation, qui est chargée par l' ldc
opération à l'étape 0
. Ensuite, le deuxième littéral de chaîne est chargé par ldc
opération à l'étape 7
. Cela n'implique pas la création d'un nouvel String
objet lors de l'exécution. La chaîne est déjà connue au moment de la compilation et elle est internée.