Quelle est la différence entre «texte» et nouvelle chaîne («texte»)?

195

Quelle est la différence entre ces deux déclarations suivantes?

String s = "text";

String s = new String("text");
user368141
la source
Rubrique connexe: JEP 192: déduplication de chaînes dans G1 .
Basil Bourque
N'importe qui, veuillez répondre à cela. String a = "Java"; Chaîne b = "Java"; System.out.println (a == b); true // mais System.out.println ("a == b?" + a == b); // false ...
Énergie
je ne comprends pas quand j'ai ajouté un commentaire ("a == b?) => mon résultat devient FAUX. pourquoi?
Energie
@Energy Le résultat est falsedû au fait que l'ordre des opérations dicte que l'opérateur + passe en premier, concaténant "a == b?" avec a pour créer une chaîne "a == b? Java". Ensuite, l'expression est "a==b?Java" == bévaluée à faux.
Allison B
@AllisonB l'a compris, merci beaucoup!
Énergie

Réponses:

187

new String("text"); crée explicitement une nouvelle instance d'un Stringobjet distincte par référence ; String s = "text";peut réutiliser une instance du pool de constantes de chaîne si elle est disponible.

Vous voudriez très rarement utiliser le new String(anotherString)constructeur. Depuis l'API:

String(String original): Initialise un objet nouvellement créé String afin qu'il représente la même séquence de caractères que l'argument; en d'autres termes, la chaîne nouvellement créée est une copie de la chaîne d'argument. À moins qu'une copie explicite de l'original ne soit nécessaire, l'utilisation de ce constructeur n'est pas nécessaire car les chaînes sont immuables.

Questions connexes


Que signifie la distinction référentielle

Examinez l'extrait de code suivant:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==sur deux types de référence est une comparaison d'identité de référence. Deux objets qui ne le equalssont pas forcément ==. Il est généralement faux d'utiliser== des types de référence; la plupart du temps equalsdoivent être utilisés à la place.

Néanmoins, si pour une raison quelconque vous devez en créer deux equalsmais pas une ==chaîne, vous pouvez utiliser le new String(anotherString)constructeur. Il faut cependant redire que c'est très particulier et que c'est rarement l'intention.

Références

Problèmes liés

polygénelubrifiants
la source
3
Si j'écris: String s = new String ("abc"); Et maintenant j'écris: String s = "abc"; Will String s = "abc"; créer un nouveau littéral de chaîne dans le pool de chaînes?
Kaveesh Kanwal
Pourquoi personne ne répond à la question précédente?
zeds
2
@KaveeshKanwal Non, le littéral ne sera pas dupliqué. Comme vous pouvez le voir, il y a 2 "abc"s. Un seul d'entre eux ira au pool de chaînes et l'autre s'y référera. Ensuite, il y saura le nouvel objet approprié.
Kayaman
1
@Kaveesh Kanwal - String s = new String ("abc") ne créera qu'un nouvel objet String avec la valeur "abc". Et la 2ème instruction vérifiera si un littéral de chaîne "abc" est déjà présent ou non dans String Pool. S'il est déjà présent, la référence à l'existant est renvoyée et sinon, un nouveau littéral ("abc") est créé dans le pool de chaînes. J'espère que cela résoudra votre question !!
user968813
Il n'y a pas de «peut» à ce sujet. Le compilateur doit regrouper les littéraux de chaîne. JLS 3.10.5 .
Marquis de Lorne
119

Les littéraux de chaîne iront dans le pool de chaînes constantes .

L'instantané ci-dessous peut vous aider à le comprendre visuellement pour vous en souvenir plus longtemps.

entrez la description de l'image ici


Création d'objet ligne par ligne:

String str1 = new String("java5");

En utilisant le littéral de chaîne "java5" dans le constructeur, une nouvelle valeur de chaîne est stockée dans le pool de constantes de chaîne. En utilisant un nouvel opérateur, un nouvel objet chaîne est créé dans le tas avec "java5" comme valeur.

String str2 = "java5"

La référence "str2" pointe vers une valeur déjà stockée dans le pool de constantes de chaîne

String str3 = new String(str2);

Un nouvel objet chaîne est créé dans le tas avec la même valeur que référence par "str2"

String str4 = "java5";

La référence "str4" pointe vers une valeur déjà stockée dans le pool de constantes de chaîne

Total des objets: tas - 2, piscine - 1

Pour en savoir plus sur la communauté Oracle

Braj
la source
1
Bonne réponse .. mais je voulais savoir que maintenant je suis en train de changer la valeur de str1 = "java6" alors cela changera la valeur de str4?
CoronaPintu
2
oui j'ai vérifié qu'il ne changera pas la valeur de str4
CoronaPintu
@Braj Pouvez-vous fournir la documentation de l'affirmation de votre réponse?
Basil Bourque
@Braj: les en-têtes du «tas» et du «pool» du tableau sont-ils censés être inversés?
Rahul Kurup du
Pas correcte. Le pool constant est créé au moment de la compilation et non au moment de l'exécution. N'utilisez pas la mise en forme des guillemets pour le texte qui n'est pas cité.
Marquis de Lorne
15

On crée une chaîne dans le pool de constantes de chaîne

String s = "text";

l'autre crée une chaîne dans le pool constant ( "text") et une autre chaîne dans l'espace de tas normal ( s). Les deux chaînes auront la même valeur, celle de "texte".

String s = new String("text");

s est alors perdu (éligible au GC) s'il n'est pas utilisé par la suite.

Les littéraux de chaîne en revanche sont réutilisés. Si vous utilisez "text"à plusieurs endroits de votre classe, ce sera en fait une et une seule chaîne (c'est-à-dire plusieurs références à la même chaîne dans le pool).


la source
Les cordes dans le pool constant ne sont jamais perdues. Vouliez-vous dire que 's' est perdu s'il n'est pas utilisé plus tard?
Marquis de Lorne
@EJP: oui, je voulais dire "s". Merci d'avoir remarqué. Je vais corriger la question.
9

JLS

Le concept est appelé "interning" par le JLS.

Passage pertinent de JLS 7 3.10.5 :

De plus, un littéral de chaîne fait toujours référence à la même instance de la classe String. En effet, les littéraux de chaîne - ou, plus généralement, les chaînes qui sont les valeurs d'expressions constantes (§15.28) - sont "internés" de manière à partager des instances uniques, en utilisant la méthode String.intern.

Exemple 3.10.5-1. Littéraux de chaîne

Le programme composé de l'unité de compilation (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

et l'unité de compilation:

package other;
public class Other { public static String hello = "Hello"; }

produit la sortie:

true true true true false true

JVMS

JVMS 7 5.1 dit :

Un littéral de chaîne est une référence à une instance de la classe String et est dérivé d'une structure CONSTANT_String_info (§4.4.3) dans la représentation binaire d'une classe ou d'une interface. La structure CONSTANT_String_info donne la séquence de points de code Unicode constituant le littéral de chaîne.

Le langage de programmation Java requiert que des littéraux de chaîne identiques (c'est-à-dire des littéraux qui contiennent la même séquence de points de code) doivent faire référence à la même instance de classe String (JLS §3.10.5). En outre, si la méthode String.intern est appelée sur une chaîne, le résultat est une référence à la même instance de classe qui serait renvoyée si cette chaîne apparaissait comme un littéral. Ainsi, l'expression suivante doit avoir la valeur true:

("a" + "b" + "c").intern() == "abc"

Pour dériver un littéral de chaîne, la machine virtuelle Java examine la séquence de points de code donnée par la structure CONSTANT_String_info.

  • Si la méthode String.intern a déjà été appelée sur une instance de classe String contenant une séquence de points de code Unicode identique à celle donnée par la structure CONSTANT_String_info, le résultat de la dérivation littérale de chaîne est une référence à cette même instance de classe String.

  • Sinon, une nouvelle instance de la classe String est créée contenant la séquence de points de code Unicode donnée par la structure CONSTANT_String_info; une référence à cette instance de classe est le résultat d'une dérivation littérale de chaîne. Enfin, la méthode interne de la nouvelle instance de String est invoquée.

Bytecode

Il est également instructif de regarder l'implémentation du bytecode sur OpenJDK 7.

Si nous décompilons:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

nous avons sur le bassin constant:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

et main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Notez comment:

  • 0et 3: la même ldc #2constante est chargée (les littéraux)
  • 12: une nouvelle instance de chaîne est créée (avec #2 comme argument)
  • 35: aet csont comparés comme des objets normaux avecif_acmpne

La représentation des chaînes constantes est assez magique sur le bytecode:

  • il a une structure CONSTANT_String_info dédiée , contrairement aux objets réguliers (par exemple new String)
  • la structure pointe vers une structure CONSTANT_Utf8_info qui contient les données. Ce sont les seules données nécessaires pour représenter la chaîne.

et la citation JVMS ci-dessus semble dire que chaque fois que l'Utf8 pointé est le même, des instances identiques sont chargées par ldc.

J'ai fait des tests similaires pour les champs et:

  • static final String s = "abc"pointe vers la table des constantes via l' attribut ConstantValue
  • les champs non finaux n'ont pas cet attribut, mais peuvent toujours être initialisés avec ldc

Conclusion : le pool de chaînes prend directement en charge le bytecode et la représentation en mémoire est efficace.

Bonus: comparez cela au pool Integer , qui n'a pas de prise en charge directe du bytecode (c'est-à-dire pas d' CONSTANT_String_infoanalogue).

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
2

@ Braj: je pense que vous avez mentionné l'inverse. S'il vous plait corrigez moi si je me trompe

Création d'objet ligne par ligne:

String str1 = new String ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

Chaîne str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

Chaîne str3 = nouvelle chaîne (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

Chaîne str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)
SDC
la source
str1n'est pas impliqué dans la valeur de str2ou str3ou str4en aucune façon.
Marquis de Lorne
1

Pensez à "bla"être une usine magique commeStrings.createString("bla") (pseudo). L'usine détient un pool de toutes les chaînes encore créé de cette façon.

S'il est invoqué, il vérifie s'il existe déjà une chaîne dans le pool avec cette valeur. S'il est vrai, il renvoie cet objet chaîne, donc les chaînes obtenues de cette façon sont en effet le même objet.

Sinon, il crée un nouvel objet chaîne en interne, l'enregistre dans le pool, puis le renvoie. Ainsi, lorsque la même valeur de chaîne est interrogée la prochaine fois, elle renvoie la même instance.

La création manuelle new String("")remplace ce comportement en contournant le pool de littéraux de chaîne. Donc, l'égalité doit toujours être vérifiée en utilisant equals()qui compare la séquence de caractères au lieu de l'égalité de référence d'objet.

b_erb
la source
La «fabrique magique» à laquelle vous faites référence n'est ni plus ni moins que le compilateur Java. C'est une erreur d'écrire ce processus comme s'il s'était produit au moment de l'exécution.
Marquis de Lorne
1

Voici une façon simple de comprendre la différence: -

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

la sortie est

s==s1 is true
s==s2 is false

Ainsi, la nouvelle chaîne () créera toujours une nouvelle instance.

Shashank T
la source
1

Tout littéral de chaîne est créé dans le pool de littéraux de chaîne et le pool n'autorise aucun doublon. Ainsi, si deux ou plusieurs objets chaîne sont initialisés avec la même valeur littérale, tous les objets pointeront vers le même littéral.

String obj1 = "abc";
String obj2 = "abc";

"obj1" et "obj2" pointeront vers le même littéral de chaîne et le pool de littéraux de chaîne n'aura qu'un seul littéral "abc".

Lorsque nous créons un objet de classe String à l'aide du nouveau mot clé, la chaîne ainsi créée est stockée dans la mémoire de tas. Tout littéral de chaîne passé en paramètre au constructeur de la classe String est cependant stocké dans le pool de chaînes. Si nous créons plusieurs objets en utilisant la même valeur avec le nouvel opérateur, un nouvel objet sera créé à chaque fois dans le tas, car ce nouvel opérateur doit être évité.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" et "obj2" pointeront vers deux objets différents dans le tas et le pool de littéraux de chaîne n'aura qu'un seul littéral "abc".

Il convient également de noter en ce qui concerne le comportement des chaînes que toute nouvelle affectation ou concaténation effectuée sur une chaîne crée un nouvel objet en mémoire.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Maintenant dans le cas ci-dessus:
Ligne 1: le littéral "abc" est stocké dans le pool de chaînes.
Ligne 2: le littéral "abcdef" est stocké dans le pool de chaînes.
Ligne 3: Un nouveau littéral "xyz" est stocké dans le pool de chaînes et "str1" commence à pointer vers ce littéral.
Ligne 4: Puisque la valeur est générée en ajoutant à une autre variable, le résultat est stocké dans la mémoire du tas et le littéral ajouté "ghi" sera vérifié pour son existence dans le pool de chaînes et sera créé car il n'existe pas dans le cas ci-dessus.

Amritansh
la source
0

Bien qu'il ait la même apparence du point de vue des programmeurs, il a un impact important sur les performances. Vous voudriez utiliser presque toujours le premier formulaire.

fastcodejava
la source
0
String str = new String("hello")

Il vérifiera si le pool de constantes String contient déjà String "bonjour"? S'il est présent, il n'ajoutera pas d'entrée dans le pool constant de chaînes. S'il n'est pas présent, il ajoutera une entrée dans le pool constant de chaînes.

Un objet sera créé dans une zone de mémoire de tas et str référence à un objet créé dans un emplacement de mémoire de tas.

si vous voulez une strréférence à un objet point contenant dans le pool de constantes String, alors il faut appeler explicitementstr.intern();

String str = "world";

Il vérifiera si le pool de constantes String contient déjà String "bonjour"? S'il est présent, il n'ajoutera pas d'entrée dans le pool constant de chaînes. S'il n'est pas présent, il ajoutera une entrée dans le pool constant de chaînes.

Dans les deux cas ci-dessus, la strréférence pointe vers la chaîne "world"présente dans le pool constant.

Jayesh
la source
'It' étant le compilateur Java. Le littéral de chaîne crée une entrée unique dans le pool constant, au moment de la compilation. C'est une erreur de désactiver ce processus comme s'il se produisait à l'exécution.
Marquis de Lorne
Pouvez-vous expliquer clairement ce qui ne va pas dans ce message?
Jayesh
Ce qui ne va pas dans ce post, c'est que le littéral de chaîne est mis en commun au moment du complle, comme je l'ai déjà dit. Pas lors de l'exécution du code, comme dans votre réponse.
Marquis de Lorne
@EJP J'apprécie votre réponse. Pouvez-vous indiquer la ligne exacte qui ne répond pas correctement? Je vois que toutes les réponses ci-dessus sont les mêmes que celles que j'ai écrites. Aidez-moi, je veux corriger ma compréhension. Merci.
Jayesh
Vous avez écrit sur l'ensemble du processus comme si tout se déroulait lors de l'exécution de la ligne de code, ce qui, comme je vous l'ai dit à plusieurs reprises, n'est pas le cas. Vous ne pouvez pas réduire tout cela à une seule «ligne exacte» en vous trompant dans votre réponse.
Marquis de Lorne
0

Lorsque vous stockez une chaîne sous

String string1 = "Hello";

directement, la JVM crée un objet String avec le prix indiqué pendant un bloc de mémoire distinct appelé pool de constantes String.

Et chaque fois que nous avons tendance à essayer de produire une autre chaîne comme

String string2 = "Hello";

La JVM vérifie s'il existe ou non un objet String à prix constant dans le pool de constantes String, si tel est le cas, plutôt que de créer un nouvel objet JVM attribue la référence de l'objet existant à la nouvelle variable.

Et quand nous stockons String sous

String string = new String("Hello");

en utilisant le nouveau mot-clé, un tout nouvel objet avec le prix donné est créé quel que soit le contenu du pool de constantes String.

Syed Babar Ali Shah 3261-FBASB
la source