Lorsque vous déclarez une variable String(qui est immuable ) en tant que finalet 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 - finalVariables :
Une variable de type ou type primitif String, c'est-à-dire finalinitialisé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 Stringsont 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 Stringvariables 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 Stringobjet. Vous pouvez le vérifier en comparant le code octet des deux codes.
Le premier exemple de code (non finalversion) 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 stret ingdans deux variables distinctes et utilise StringBuilderpour effectuer l'opération de concaténation.
Alors que votre deuxième exemple de code ( finalversion) 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 stringau moment de la compilation, qui est chargée par l' ldcopération à l'étape 0. Ensuite, le deuxième littéral de chaîne est chargé par ldcopération à l'étape 7. Cela n'implique pas la création d'un nouvel Stringobjet lors de l'exécution. La chaîne est déjà connue au moment de la compilation et elle est internée.