J'ai une question simple sur les chaînes en Java. Le segment de code simple suivant concatène simplement deux chaînes, puis les compare avec ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
L'expression de comparaison concat=="string"
revient false
comme évidente (je comprends la différence entre equals()
et ==
).
Lorsque ces deux chaînes sont déclarées final
comme telles,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
L'expression de comparaison concat=="string"
, dans ce cas, revient true
. Pourquoi cela final
fait-il une différence? Faut-il faire quelque chose avec la réserve de stagiaires ou je suis juste induit en erreur?
equals()
et==
dans le contexte des chaînes et pose une question plus significative.String
? Je pense qu'il est très logique, pas idiot, d'avoir la comparaison de contenu effectuée parequals
, une méthode que nous pouvons remplacer pour dire quand nous considérons deux objets égaux, et de faire la comparaison d'identité par==
. Si la comparaison du contenu était faite par==
nous ne pouvions pas passer outre pour définir ce que nous entendons par "contenu égal", et avoir le sens deequals
et==
inversé uniquement pourString
s serait idiot. De plus, malgré cela, je ne vois aucun avantage à==
faire la comparaison de contenu à la place deequals
.Réponses:
Lorsque vous déclarez une variable
String
(qui est immuable ) en tant quefinal
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:qui, comparé à
"string"
, vous donneratrue
, car les littéraux de chaîne sont internés .De JLS §4.12.4 -
final
Variables :Aussi de JLS §15.28 - Expression constante:
Ce n'est pas le cas dans votre premier exemple de code, où les
String
variables ne le sont pasfinal
. 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 nouvelString
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:De toute évidence, il stocke
str
eting
dans deux variables distinctes et utiliseStringBuilder
pour effectuer l'opération de concaténation.Alors que votre deuxième exemple de code (
final
version) ressemble à ceci: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'étape0
. Ensuite, le deuxième littéral de chaîne est chargé parldc
opération à l'étape7
. Cela n'implique pas la création d'un nouvelString
objet lors de l'exécution. La chaîne est déjà connue au moment de la compilation et elle est internée.la source
true
?String
objet est nouvellement créé (§12.5) à moins que l'expression ne soit une expression constante (§15.28). »Littéralement, l'application d'une optimisation dans la version non finale n'est pas autorisée, car une chaîne« nouvellement créée »doit avoir une identité d'objet différente. Je ne sais pas si c'est intentionnel. Après tout, la stratégie de compilation actuelle consiste à déléguer à une fonction d'exécution qui ne documente pas ces restrictions.Selon mes recherches, tous
final String
sont internés en Java. Depuis l'un des articles du blog:Cela signifie donc que si vous appelez,
String.intern()
vous pouvez comparer deux chaînes à l'aide de l'==
opérateur. Mais ici, ceString.intern()
n'est pas nécessaire car en Javafinal String
sont internés en interne.Vous pouvez trouver plus d'informations Comparaison de chaînes en utilisant l'opérateur == et Javadoc pour la méthode String.intern () .
Consultez également ce post Stackoverflow pour plus d'informations.
la source
Si vous regardez ces méthodes
et son décompilé avec des
javap -c ClassWithTheseMethods
versions que vous verrezet
Donc, si les chaînes ne sont pas le compilateur final devra utiliser
StringBuilder
pour concaténerstr1
etstr2
ainsisera compilé pour
ce qui signifie qu'il
concat
sera créé au moment de l'exécution et ne proviendra donc pas du pool de chaînes.De plus, si les chaînes sont finales, le compilateur peut supposer qu'elles ne changeront jamais, au lieu de l'utiliser,
StringBuilder
il peut concaténer ses valeurs en toute sécurité afinpeut être changé en
et concaténé en
ce qui signifie que
concate
cela deviendra le littéral sting qui sera interné dans le pool de chaînes, puis comparé au même littéral de chaîne de ce pool dans l'if
instruction.la source
Concept de piscine de conts de pile et de chaîne
la source
Voyons un code d'octet pour l'
final
exempleÀ
0:
et2:
, leString
"string"
est poussé sur la pile (à partir du pool constant) et stockéconcat
directement dans la variable locale . Vous pouvez en déduire que le compilateur crée (concatène) leString
"string"
lui - même au moment de la compilation.Le
final
code non octetIci, vous avez deux
String
constantes,"str"
et"ing"
qui doivent être concaténées au moment de l'exécution avec aStringBuilder
.la source
Cependant, lorsque vous créez en utilisant la notation littérale String de Java, il appelle automatiquement la méthode intern () pour placer cet objet dans le pool String, à condition qu'il ne soit pas déjà présent dans le pool.
Le compilateur sait que la variable finale ne changera jamais, lorsque nous ajoutons ces variables finales, la sortie va au pool de chaînes car la
str1 + str2
sortie d'expression ne changera jamais non plus, donc le compilateur appelle finalement inter méthode après la sortie des deux variables finales ci-dessus. Dans le cas d'un compilateur de variables non final, n'appelez pas la méthode interne.la source