intern () se comportant différemment en Java 6 et Java 7

class Test { public static void main(Ssortingng...args) { Ssortingng s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1.intern()); Ssortingng s2 = "Goodmorning"; if (s1 == s2) { System.out.println("both are equal"); } } } 

Ce code produit différentes sorties en Java 6 et Java 7. En Java 6, la condition s1==s2 renvoie false et en Java 7, s1==s2 renvoie true . Pourquoi?

Pourquoi ce programme produit-il des sorties différentes en Java 6 et Java 7?

Il semble que JDK7 traite les stagiaires d’une manière différente.
Je l’ai testé avec la version 1.7.0-b147 et obtenu “les deux sont égaux”, mais lorsque je l’exécute (même bytecode) avec 1,6.0_24, je ne reçois pas le message.
Cela dépend aussi de l’emplacement de la ligne Ssortingng b2 =... dans le code source. Le code suivant ne génère pas non plus le message:

 class Test { public static void main(Ssortingng... args) { Ssortingng s1 = "Good"; s1 = s1 + "morning"; Ssortingng s2 = "Goodmorning"; System.out.println(s1.intern()); //just changed here s1.intern() and the if condition runs true if(s1 == s2) { System.out.println("both are equal"); } //now it works. } } 

il semble que le intern après ne pas avoir trouvé la chaîne dans son pool de chaînes, insère l’instance réelle s1 dans le pool. La JVM utilise ce pool lors de la création de s2, ce qui lui permet d’obtenir la même référence que s1. D’un autre côté, si s2 est créé en premier, cette référence est stockée dans le pool.
Cela peut être dû au déplacement des chaînes internes de la génération permanente du segment de mémoire Java.

Trouvé ici: RFE importantes adressées dans JDK 7

Dans JDK 7, les chaînes internes ne sont plus allouées dans la génération permanente du segment de mémoire Java, mais sont plutôt allouées dans la partie principale du segment de mémoire Java (appelées générations jeunes et anciennes), avec les autres objects créés par l’application. . Cette modification se traduira par plus de données résidant dans le segment de mémoire principal Java et moins de données dans la génération permanente, ce qui peut nécessiter un ajustement de la taille des segments. La plupart des applications ne verront que des différences relativement faibles dans l’utilisation du tas en raison de ce changement, mais les applications plus volumineuses qui chargent de nombreuses classes ou qui utilisent beaucoup la méthode Ssortingng.intern () verront des différences plus significatives.

Je ne sais pas si c’est un bogue et de quelle version … Le JLS 3.10.5 indique

Le résultat de l’internalisation explicite d’une chaîne calculée est la même chaîne que toute chaîne littérale existante avec le même contenu.

La question est donc de savoir comment le préexistant est interprété, au moment de la compilation ou au moment de l’exécution: “Goodmorning” est-il préexistant ou non?
Je préfère la façon dont il a été mis en œuvre avant 7 …

Supprimons les détails inutiles de l’exemple:

 class Test { public static void main(Ssortingng... args) { Ssortingng s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } } 

Considérons Ssortingng#intern comme une boîte noire. Sur la base de quelques cas de test exécutés, je conclurais que la mise en œuvre est la suivante:

Java 6:
Si le pool contient un object égal à this , renvoyez la référence à cet object, sinon créez une nouvelle chaîne (égale à this ), mettez-la dans le pool et renvoyez la référence à cette instance créée.

Java 7:
Si le pool contient un object égal à this , renvoyez la référence à cet object, sinon mettez this dans le pool et renvoyez this .

Ni Java 6 ni Java 7 ne rompent le contrat de la méthode .

Il semble que le nouveau comportement de la méthode interne résulte du correctif de ce bogue: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931 .

== compare les références. La méthode intern vérifie que les chaînes de même valeur ont la même référence.

Le javadoc de la méthode Ssortingng.intern explique:

Intern stagiaire ()

Renvoie une représentation canonique pour l’object ssortingng.

Un pool de chaînes, initialement vide, est géré en privé par la classe Ssortingng.

Lorsque la méthode intern est appelée, si le pool contient déjà une chaîne égale à cet object Ssortingng, déterminée par la méthode equals (Object), la chaîne du pool est renvoyée. Sinon, cet object Ssortingng est ajouté au pool et une référence à cet object Ssortingng est renvoyée.

Il en résulte que pour deux chaînes s et t, s.intern () == t.intern () est vrai si et seulement si s.equals (t) est vrai.

Toutes les chaînes littérales et les expressions constantes à valeur de chaîne sont internées. Les littéraux de chaîne sont définis au §3.10.5 de la spécification de langage Java

Retourne: une chaîne qui a le même contenu que cette chaîne, mais qui est garantie d’un pool de chaînes uniques.

Donc, sans interner le compilateur examine les constantes du code Java et construit son pool constant à partir de celui-ci. Il existe un pool différent géré par la classe Ssortingng et interning vérifie la chaîne transmise au pool et vérifie que la référence est unique (de sorte que == fonctionne).

Dans jdk6: Ssortingng s1="Good"; crée un object Ssortingng “Good” dans un pool constant.

s1=s1+"morning"; crée un autre object Ssortingng “morning” dans le pool constant, mais cette fois en fait JVM fait: s1=new SsortingngBuffer().append(s1).append("morning").toSsortingng(); .

Maintenant que le new opérateur crée un object dans le tas, la référence dans s1 est celle du tas non constant et de la Ssortingng s2="Goodmorning"; crée un object Ssortingng “Goodmorning” dans un pool constant dont la référence est stockée dans s2 .

Par conséquent, if(s1==s2) condition est fausse.

Mais que se passe-t-il dans jdk7?

PREMIER CAS:

Dans le premier code, vous ajoutez trois chaînes dans le pool de chaînes. 1. s1 = “bon”
2. s1 = “Goodmorning” (après concaténation) 3. s2 = “Goodmorining”

En faisant si (s1 == s2), les objects sont identiques mais la référence est différente, donc elle est fausse.

SECOND CAS:

Dans ce cas, vous utilisez s1.intern (), ce qui implique que si le pool contient déjà une chaîne égale à cet object Ssortingng, déterminée par la méthode equals (Object), la chaîne du pool est renvoyée. Sinon, cet object Ssortingng est ajouté au pool et une référence à cet object Ssortingng est renvoyée.

  1. s1 = “bien”
  2. s1 = “Goodmorning” (après concaténation)
  3. Pour Ssortingng s2 = “Goodmorning”, la nouvelle chaîne n’est pas ajoutée au pool et vous obtenez une référence à celle existante pour s2. Par conséquent, si (s1 == s2) renvoie true.

Vous devez utiliser s1.equals(s2) . L’utilisation de == avec des objects Ssortingng compare les références d’object elles-mêmes.

Edit: Lorsque je lance votre deuxième extrait de code, je ne suis pas imprimé “les deux sont égaux”.

Edit2: Précisé que les références sont comparées lorsque vous utilisez ‘==’.

il y a principalement 4 façons de comparer la chaîne:

  1. “== opérateur”: il compare simplement la variable de référence de l’object chaîne. Donc, cela pourrait vous donner des résultats inattendus selon la manière dont vous avez créé la chaîne, c’est-à-dire en utilisant le constructeur de la classe Ssortingng ou simplement en utilisant des guillemets car les deux obtiennent une mémoire différente (respectivement en tas et en pool).
  2. “equals (Object) method”: c’est la méthode de la classe d’object et elle est OVERLOADED par la classe de chaîne. Il compare la chaîne entière et IS CASE SENSITIVE.
  3. “méthode equalsIgnoreCase (Ssortingng)”: il s’agit d’une méthode de classe de chaîne qui compare la chaîne entière et IS NOT CASE SENSITIVE.
  4. “compares (Ssortingng) method”: compare les deux chaînes caractère par caractère et renvoie leur différence si la valeur renvoyée est 0, cela signifie que les chaînes sont égales.

Lorsque vous comparez deux chaînes, n’utilisez pas == et utilisez eqauls() car vous comparez des objects et non des références:

 ssortingng1.equals(ssortingng2); 

Le code de résultat dépend de l’exécution:

 class Test { public static void main(Ssortingng... args) { Ssortingng s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } } 

Si vous écrivez comme ceci:

 class Test { public static void main(Ssortingng... args) { Ssortingng s = "GoodMorning"; Ssortingng s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6. } } 

La raison en est ‘ldc #N’ (Charger la chaîne du pool constant) et Ssortingng.intern () utilisera les deux SsortingngTable dans la JVM du hotspot. Pour plus de détails, j’ai écrit un article en anglais sur la piscine: http://aprilsoft.cn/blog/post/307.html