Les chaînes sont des objets en Java, alors pourquoi n'utilisons-nous pas «nouveau» pour les créer?


104

Nous créons normalement des objets en utilisant le newmot - clé, comme:

Object obj = new Object();

Les chaînes sont des objets, mais nous ne les utilisons pas newpour les créer:

String str = "Hello World";

Pourquoi est-ce? Puis-je créer une chaîne avec new?


Vous devriez également jeter un oeil sur cette question stackoverflow.com/questions/456575/java-wrapper-equality-test
modifiée le

1
Parce que les littéraux de chaîne sont déjà des objets.
Marquis of Lorne

1
Notez que cela new String(...)a été utilisé pour contourner un détail d'implémentation lors de la sous-chaîne de chaînes volumineuses. Cela a été corrigé dans Java 7 et n'est plus nécessaire.
Thorbjørn Ravn Andersen

Je suis le 100e liker de ce post. :)
Mukit09

Réponses:


130

En plus de ce qui a déjà été dit, les chaînes littérales [c'est-à-dire, les chaînes comme "abcd"mais pas comme new String("abcd")] en Java sont internées - cela signifie que chaque fois que vous faites référence à "abcd", vous obtenez une référence à une seule Stringinstance, plutôt qu'à une nouvelle chaque fois. Vous aurez donc:

String a = "abcd";
String b = "abcd";

a == b; //True

mais si tu avais

String a = new String("abcd");
String b = new String("abcd");

alors il est possible d'avoir

a == b; // False

(et au cas où quelqu'un aurait besoin d'un rappel, utilisez toujours .equals()pour comparer les chaînes; ==teste l'égalité physique).

L'intégration de littéraux String est une bonne chose car ils sont souvent utilisés plus d'une fois. Par exemple, considérez le code (artificiel):

for (int i = 0; i < 10; i++) {
  System.out.println("Next iteration");
}

Si nous n'avions pas d'internement des chaînes, la "prochaine itération" devrait être instanciée 10 fois, alors que maintenant elle ne sera instanciée qu'une seule fois.


1
En utilisant String a = new String ("abcd"), cela signifie-t-il que deux chaînes avec un contenu similaire sont présentes en mémoire.
modifié le

1
Bien - le compilateur ne vérifiera pas nécessairement si une telle chaîne a déjà été internée (bien que vous puissiez certainement en écrire une qui l'a fait).
danben

oui, cette optimisation est possible car les chaînes sont immuables et peuvent donc être partagées sans problème. la gestion partagée "asdf" est une implémentation du modèle de conception 'Flyweight'.
manuel aldana

Personne n'a dit que ce n'était pas possible, seulement que ce n'était pas garanti. Était-ce votre vote négatif?
danben

Qu'entendez-vous par "== tests d'égalité des objets"? Cela ne me semble pas vrai, mais peut-être que vous vouliez dire quelque chose de différent de ce que cela semble vouloir dire.
Dawood ibn Kareem

32

Les chaînes sont des objets "spéciaux" en Java. Les concepteurs Java ont judicieusement décidé que les chaînes sont utilisées si souvent qu'ils avaient besoin de leur propre syntaxe ainsi que d'une stratégie de mise en cache. Lorsque vous déclarez une chaîne en disant:

String myString = "something";

myString est une référence à un objet String avec une valeur de "quelque chose". Si vous déclarez plus tard:

String myOtherString = "something";

Java est suffisamment intelligent pour déterminer que myString et myOtherString sont identiques et les stockera dans une table de chaînes globale en tant que même objet. Cela repose sur le fait que vous ne pouvez pas modifier les chaînes pour ce faire. Cela réduit la quantité de mémoire requise et peut accélérer les comparaisons.

Si, à la place, vous écrivez

String myOtherString = new String("something");

Java créera pour vous un tout nouvel objet, distinct de l'objet myString.


Hé ... il n'est pas nécessaire d'avoir une "sagesse infinie" pour reconnaître le besoin d'une sorte de support syntaxique pour les chaînes littérales. Presque toutes les autres conceptions de langage de programmation sérieuses prennent en charge une sorte de littéral de chaîne.
Stephen C

12
Hyperbole a été réduite à étourdir, capitaine :)
Jamie McCrindle

16
String a = "abc"; // 1 Object: "abc" added to pool

String b = "abc"; // 0 Object: because it is already in the pool

String c = new String("abc"); // 1 Object

String d = new String("def"); // 1 Object + "def" is added to the Pool

String e = d.intern(); // (e==d) is "false" because e refers to the String in pool

String f = e.intern(); // (f==e) is "true" 

//Total Objects: 4 ("abc", c, d, "def").

J'espère que cela dissipe quelques doutes. :)


String d = new String ("def"); // 1 objet + "def" est ajouté au pool -> ici "def" serait ajouté au pool uniquement s'il n'y est pas encore
southerton

@southerton sans signification. Il est déjà dans la piscine. Il a été placé là par le compilateur.
Marquis of Lorne

@EJP pourquoi (e == d) est faux ici? ils font tous les deux référence au même objet "def" dans la piscine non?
Raja

String c = nouvelle chaîne ("abc"); // 1 Objet ... cette déclaration est-elle correcte? Si "abc" est déjà référencé à partir du pool de constantes, à quoi sert la méthode inter?
Prashanth Debbadwar

6

C'est un raccourci. Ce n'était pas comme ça à l'origine, mais Java l'a changé.

Cette FAQ en parle brièvement. Le guide de spécification Java en parle également. Mais je ne peux pas le trouver en ligne.


2
Lien rompu, et je ne suis au courant d'aucune autre preuve qu'il ait jamais été changé.
Marquis of Lorne

1
@EJP C'est toujours dans la machine de retour si cela est utile.
Arjan

6

La chaîne est soumise à quelques optimisations (faute d'une meilleure phrase). Notez que String a également une surcharge d'opérateur (pour l'opérateur +) - contrairement à d'autres objets. C'est donc un cas très spécial.


1
Le + est en fait un opérateur qui est traduit en un appel StringBuilder.append (..).
whiskeysierra

5

Nous utilisons généralement des littéraux String pour éviter de créer des objets inutiles. Si nous utilisons un nouvel opérateur pour créer un objet String, il créera un nouvel objet à chaque fois.

Exemple:

String s1=“Hello“;
String s2=“Hello“;
String s3= new String(“Hello“);
String s4= new String(“Hello“);

Pour le code ci-dessus en mémoire:

entrez la description de l'image ici


2

En Java, les chaînes sont un cas particulier, avec de nombreuses règles qui ne s'appliquent qu'aux chaînes. Les guillemets doubles obligent le compilateur à créer un objet String. Étant donné que les objets String sont immuables, cela permet au compilateur d'interner plusieurs chaînes et de créer un pool de chaînes plus grand. Deux constantes String identiques auront toujours la même référence d'objet. Si vous ne voulez pas que ce soit le cas, vous pouvez utiliser le nouveau String (""), et cela créera un objet String au moment de l'exécution. La méthode intern () était autrefois courante, pour que les chaînes créées dynamiquement soient vérifiées par rapport à la table de recherche de chaînes. Une fois qu'une chaîne est internée, la référence d'objet pointe vers l'instance de chaîne canonique.

    String a = "foo";
    String b = "foo";
    System.out.println(a == b); // true
    String c = new String(a);
    System.out.println(a == c); // false
    c = c.intern();
    System.out.println(a == c); // true

Lorsque le chargeur de classe charge une classe, toutes les constantes String sont ajoutées au pool String.


"Les guillemets doubles obligent le compilateur à créer un objet String." commentaire sous-évalué
csguy

0

Sucre syntaxique. le

String s = new String("ABC");

la syntaxe est toujours disponible.


1
Ceci n'est pas tout à fait vrai. s = new String ("ABC") ne vous donnera pas les mêmes résultats que s = "ABC". Voir le commentaire de danben.
Steve B.

2
Aussi, quelque peu ironiquement, il créera d'abord une instance de String représentant "ABC" en ligne - puis la passera comme argument à l'appel du constructeur qui créera un retour d'une chaîne de valeur identique.
Andrzej Doyle

1
Le cas d'utilisation valide pour ce constructeur est String small = new String(huge.substring(int, int));, qui vous permet de recycler le gros sous-jacent à char[]partir de la hugechaîne d' origine .
Pascal Thivent du

1
@PascalThivent oui mais plus avec Java 8. Il ne partage plus les tableaux (en préparation pour d'autres optimisations comme la déduplication automatique des chaînes avec G1 ou la prochaine compression de chaînes).
eckes

@AndrzejDoyle Pas correct. Le compilateur crée l'objet pour le littéral.
Marquis of Lorne

0

Vous pouvez toujours utiliser new String("string"), mais il serait plus difficile de créer de nouvelles chaînes sans littéraux de chaîne ... vous devrez utiliser des tableaux de caractères ou des octets :-) Les littéraux de chaîne ont une propriété supplémentaire: tous les mêmes littéraux de chaîne de n'importe quelle classe pointent vers la même chaîne instance (ils sont internés).


0

Il n'est presque pas nécessaire de créer une nouvelle chaîne car le littéral (les caractères entre guillemets) est déjà un objet String créé lorsque la classe hôte est chargée. Il est parfaitement légal d'invoquer des méthodes sur un littéral et don, la principale distinction est la commodité fournie par les littéraux. Ce serait une douleur et un gaspillage majeurs si nous devions créer un tableau de caractères et le remplir char par char et faire un nouveau String (char array).


0

N'hésitez pas à créer une nouvelle chaîne avec

String s = new String("I'm a new String");

La notation habituelle s = "new String";est plus ou moins un raccourci pratique - qui devrait être utilisé pour des raisons de performances, sauf dans les cas assez rares, où vous avez vraiment besoin de chaînes qui se qualifient pour l'équation

(string1.equals(string2)) && !(string1 == string2)

ÉDITER

En réponse au commentaire: il ne s'agissait pas d'un conseil, mais simplement d'une réponse directe à la thèse des questionneurs, à savoir que nous n'utilisons pas le mot-clé «nouveau» pour les chaînes, ce qui n'est tout simplement pas vrai. J'espère que cette modification (y compris celle ci-dessus) clarifie un peu cela. BTW - il y a quelques bonnes et bien meilleures réponses à la question ci-dessus sur SO.


4
-1 - Mauvais conseil. Vous ne devez PAS «vous sentir libre» d'utiliser À new String(...)MOINS QUE votre application ne vous oblige à créer une chaîne avec une identité distincte.
Stephen C

1
Je le sais. Modification du message pour clarification.
Andreas Dolk

0

Le pool littéral contient toutes les chaînes créées sans utiliser le mot clé new.

Il y a une différence: la chaîne sans nouvelle référence est stockée dans le pool littéral de chaîne et la chaîne avec new indique qu'elles sont dans la mémoire du tas.

Les chaînes avec new sont ailleurs en mémoire, comme n'importe quel autre objet.


0

Parce que String est une classe immuable dans java.

Maintenant, pourquoi est-il immuable? Comme String est immuable, il peut être partagé entre plusieurs threads et nous n'avons pas besoin de synchroniser l'opération String en externe. As String est également utilisé dans le mécanisme de chargement de classe. Donc, si String était mutable, java.io.writer aurait pu être changé en abc.xyz.mywriter


0
TString obj1 = new TString("Jan Peter");                
TString obj2 = new TString("Jan Peter");                    

if (obj1.Name == obj2.Name)                 
    System.out.println("True");
else                
    System.out.println("False");

Production:

Vrai

J'ai créé deux objets séparés, tous deux ont un champ (ref) 'Nom'. Donc même dans ce cas "Jan Peter" est partagé, si je comprends bien la façon dont java traite ..


0

Eh bien, le StringPool est implémenté en utilisant The Hashmap en java. Si nous créons toujours avec un nouveau mot-clé, il ne recherche pas dans le pool de chaînes et ne crée pas une nouvelle mémoire pour celui-ci, ce qui pourrait être nécessaire plus tard si une opération gourmande en mémoire est en cours d'exécution et si nous créons toutes les chaînes avec un nouveau mot-clé qui affecterait les performances de notre application. Il est donc conseillé de ne pas utiliser de nouveaux mots-clés pour créer une chaîne, car alors seulement il ira au pool de chaînes qui à son tour est un Hashmap, (mémoire enregistrée, imaginez si nous avons beaucoup de chaînes créées avec un nouveau mot-clé) ici, il sera stocké et si la chaîne existe déjà, sa référence (qui résiderait généralement dans la mémoire de la pile) serait renvoyée à la chaîne nouvellement créée. C'est donc fait pour améliorer les performances.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.