Java Strings: "String s = nouvelle chaîne ("silly");"


Je suis un gars C++ apprenant Java. Je lis du Java efficace et quelque chose m'a confondu. Il dit de ne jamais écrire de code comme ceci:

String s = new String("silly");

Parce qu'il crée des objets String inutiles. Mais au lieu de cela, il devrait être écrit comme ceci:

String s = "No longer silly";

Ok très bien jusqu'à présent...Cependant, étant donné cette classe:

public final class CaseInsensitiveString {
    private String s;
    public CaseInsensitiveString(String s) {
        if (s == null) {
            throw new NullPointerException();
        }
        this.s = s;
    }
    :
    :
}

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
  1. Pourquoi la première déclaration est-elle ok? Ne devrait-il pas être

    CaseInsensitiveString cis = "Polish";

  2. Comment faire en sorte que CaseInsensitiveString se comporte comme String donc l'instruction ci-dessus est OK (avec et sans étendre String)? Qu'est-ce que c'est à propos de la chaîne qui le rend CORRECT de pouvoir simplement lui passer un littéral comme ça? D'après ma compréhension, il n'y a pas de concept de "constructeur de copie" en Java?

Author: vaxquis, 2008-12-02

23 answers

String est une classe intégrée spéciale de la langue. C'est pour la classe String seulement dans laquelle vous devriez éviter de dire

String s = new String("Polish");

Parce que le littéral "Polish" est déjà de type String, et vous créez un objet supplémentaire inutile. Pour toute autre classe, en disant

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

Est la bonne (et seule, dans ce cas) chose à faire.

 107
Author: Adam Rosenfield, 2008-12-02 16:21:18

Je crois que le principal avantage de l'utilisation de la forme littérale (c'est-à-dire "foo" plutôt que new String("foo")) est que tous les littéraux de chaîne sont "internés" par la machine virtuelle. En d'autres termes, il est ajouté à un pool de telle sorte que tout autre code qui crée la même chaîne utilisera la chaîne regroupée plutôt que de créer une nouvelle instance.

Pour illustrer, le code suivant affichera true pour la première ligne, mais false pour la seconde:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));
 53
Author: Leigh, 2008-12-02 16:26:05

Les chaînes sont traitées un peu spécialement en java, elles sont immuables, il est donc sûr qu'elles soient traitées par comptage de références.

Si vous écrivez

String s = "Polish";
String t = "Polish";

Alors s et t se réfèrent en fait au même objet, et s==t retournera true, pour "==" pour les objets lus "est le même objet" (ou peut, de toute façon, je ne sais pas si cela fait partie de la spécification de langage réelle ou simplement un détail de l'implémentation du compilateur-alors peut-être qu'il n'est pas sûr de

Si vous écrivez

String s = new String("Polish");
String t = new String("Polish");

Puis s!=t (parce que vous avez explicitement créé une nouvelle chaîne) bien que s. equals (t) renvoie true (parce que string ajoute ce comportement à equals).

La chose que vous voulez écrire,

CaseInsensitiveString cis = "Polish";

Ne peut pas fonctionner parce que vous pensez que les citations sont une sorte de constructeur de court-circuit pour votre objet, alors qu'en fait cela ne fonctionne que pour le vieux java.lang.Chaîne.

 30
Author: Steve B., 2008-12-02 16:36:23
String s1="foo";

Littéral ira dans pool et s1 fera référence.

String s2="foo";

Cette fois, il vérifiera que le littéral "foo" est déjà disponible dans StringPool ou non car il existe maintenant, donc s2 référera le même littéral.

String s3=new String("foo");

" foo "littéral sera créé dans StringPool d'abord, puis via le constructeur string arg String Object sera créé, c'est-à-dire" foo " dans le tas en raison de la création d'objet via un nouvel opérateur, puis s3 le référera.

String s4=new String("foo");

Identique à s3

, Donc System.out.println(s1==s2);// **true** due to literal comparison.

Et System.out.println(s3==s4);// **false** due to object

Comparaison (s3 et s4 sont créés à différents endroits dans le tas)

 19
Author: Vikas, 2016-02-07 09:40:53

Strings sont spéciaux en Java-ils sont immuables et les constantes de chaîne sont automatiquement transformées en objets String.

Il n'y a aucun moyen pour votre exemple SomeStringClass cis = "value" de s'appliquer à une autre classe.

Vous ne pouvez pas non plus étendre String, car il est déclaré comme final, ce qui signifie qu'aucune sous-classe n'est autorisée.

 12
Author: Alnitak, 2008-12-02 16:25:36

Les chaînes Java sont intéressantes. Il semble que les réponses ont couvert certains des points intéressants. Voici mes deux cents.

Les Chaînes sont immuables (vous ne pouvez jamais changer)

String x = "x";
x = "Y"; 
  • La première ligne créera une variable x qui contiendra la valeur de chaîne "x". La JVM regardera dans son pool de valeurs de chaîne et verra si" x " existe, si c'est le cas, elle y pointera la variable x, si elle n'existe pas, elle la créera, puis fera le affectation
  • La deuxième ligne supprimera la référence à "x" et verra si "Y" existe dans le pool de valeurs de chaîne. S'il existe, il l'assignera, s'il ne le fait pas, il le créera d'abord, puis affectation. Lorsque les valeurs de chaîne sont utilisées ou non, l'espace mémoire dans le pool de valeurs de chaîne sera récupéré.

Les comparaisons de chaînes sont conditionnels à ce que vous comparez

String a1 = new String("A");

String a2 = new String("A");
  • a1 n'est pas égal à a2
  • a1 et a2 sont des références d'objets
  • Lorsque string est explicitement déclaré, de nouvelles instances sont créées et leurs références ne seront pas les mêmes.

Je pense que vous êtes sur le mauvais chemin en essayant d'utiliser la classe caseinsensitive. Laissez les cordes tranquilles. Ce qui vous intéresse vraiment, c'est la façon dont vous affichez ou comparez les valeurs. Utilisez une autre classe pour formater la chaîne ou pour faire des comparaisons.

C'est-à-dire

TextUtility.compare(string 1, string 2) 
TextUtility.compareIgnoreCase(string 1, string 2)
TextUtility.camelHump(string 1)

Puisque vous composez la classe, vous pouvez faire les comparaisons ce que vous voulez - comparez les valeurs de texte.

 7
Author: mson, 2012-04-12 13:13:01

Vous ne pouvez pas. Les choses entre guillemets en Java sont spécialement reconnues par le compilateur en tant que chaînes, et malheureusement vous ne pouvez pas remplacer cela (ou étendre java.lang.String - il est déclaré final).

 6
Author: Dan Vinton, 2008-12-02 16:20:00

La meilleure façon de répondre à votre question serait de vous familiariser avec le "pool de constantes de chaîne". En java, les objets string sont immuables (c'est-à-dire que leurs valeurs ne peuvent pas être modifiées une fois initialisées), donc lors de l'édition d'un objet string, vous finissez par créer un nouvel objet string édité où l'ancien objet flotte simplement dans une mémoire spéciale appelée "string constant pool". création d'un nouvel objet string par

String s = "Hello";

Ne créera qu'un objet string dans le pool et la référence s s'y référera, mais en utilisant

String s = new String("Hello");

Vous créez deux objets string: un dans la piscine, et l'autre dans le tas. la référence fera référence à l'objet dans le tas.

 6
Author: WickeD, 2012-05-19 04:30:33

- Comment faire en sorte que CaseInsensitiveString se comporte comme une chaîne afin que l'instruction ci-dessus soit ok (avec et sans chaîne d'extension)? Qu'est-ce que c'est à propos de la chaîne qui le rend correct de pouvoir simplement lui passer un littéral comme ça? D'après ma compréhension, il n'y a pas de concept de "constructeur de copie" en Java, non?

Assez a été dit dès le premier point. "Polish" est un littéral de chaîne et ne peut pas être affecté à la classe CaseInsentiviveString.

Maintenant sur le deuxième point

Bien que vous ne puissiez pas créer de nouveaux littéraux, vous pouvez suivre le premier élément de ce livre pour une approche "similaire" afin que les instructions suivantes soient vraies:

    // Lets test the insensitiveness
    CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
    CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

    assert cis5 == cis6;
    assert cis5.equals(cis6);

Voici le code.

C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java
import java.util.Map;
import java.util.HashMap;

public final class CaseInsensitiveString  {


    private static final Map<String,CaseInsensitiveString> innerPool 
                                = new HashMap<String,CaseInsensitiveString>();

    private final String s;


    // Effective Java Item 1: Consider providing static factory methods instead of constructors
    public static CaseInsensitiveString valueOf( String s ) {

        if ( s == null ) {
            return null;
        }
        String value = s.toLowerCase();

        if ( !CaseInsensitiveString.innerPool.containsKey( value ) ) {
             CaseInsensitiveString.innerPool.put( value , new CaseInsensitiveString( value ) );
         }

         return CaseInsensitiveString.innerPool.get( value );   
    }

    // Class constructor: This creates a new instance each time it is invoked.
    public CaseInsensitiveString(String s){
        if (s == null) {
            throw new NullPointerException();
         }         
         this.s = s.toLowerCase();
    }

    public boolean equals( Object other ) {
         if ( other instanceof CaseInsensitiveString ) {
              CaseInsensitiveString otherInstance = ( CaseInsensitiveString ) other;
             return this.s.equals( otherInstance.s );
         }

         return false;
    }


    public int hashCode(){
         return this.s.hashCode();
    }

// Testez la classe en utilisant le mot-clé" assert "

    public static void main( String [] args ) {

        // Creating two different objects as in new String("Polish") == new String("Polish") is false
        CaseInsensitiveString cis1 = new CaseInsensitiveString("Polish");
        CaseInsensitiveString cis2 = new CaseInsensitiveString("Polish");

        // references cis1 and cis2 points to differents objects.
        // so the following is true
        assert cis1 !=  cis2;      // Yes they're different
        assert cis1.equals(cis2);  // Yes they're equals thanks to the equals method

        // Now let's try the valueOf idiom
        CaseInsensitiveString cis3 = CaseInsensitiveString.valueOf("Polish");
        CaseInsensitiveString cis4 = CaseInsensitiveString.valueOf("Polish");

        // References cis3 and cis4 points to same  object.
        // so the following is true
        assert cis3 == cis4;      // Yes they point to the same object
        assert cis3.equals(cis4); // and still equals.

        // Lets test the insensitiveness
        CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
        CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

        assert cis5 == cis6;
        assert cis5.equals(cis6);

        // Futhermore
        CaseInsensitiveString cis7 = CaseInsensitiveString.valueOf("SomethinG");
        CaseInsensitiveString cis8 = CaseInsensitiveString.valueOf("someThing");

        assert cis8 == cis5 && cis7 == cis6;
        assert cis7.equals(cis5) && cis6.equals(cis8);
    }

}

C:\oreyes\samples\java\insensitive>javac CaseInsensitiveString.java


C:\oreyes\samples\java\insensitive>java -ea CaseInsensitiveString

C:\oreyes\samples\java\insensitive>

C'est-à-dire, créez un pool interne d'objets CaseInsensitiveString et renvoyez l'instance corrensponding à partir de là.

De cette façon, l'opérateur "==" renvoie true pour deux références d'objets représentant de la même valeur.

Ceci est utile lorsque des objets similaires sont utilisés très fréquemment et que le coût de création est coûteux.

La classe string documentation indique que la classe utilise un pool interne

La classe n'est pas complète, des problèmes intéressants se posent lorsque nous essayons de parcourir le contenu de l'objet lors de l'implémentation de l'interface CharSequence, mais ce code est assez bon pour montrer comment cet élément du livre pourrait être appliqué.

Il est important de noter qu'en utilisant l'objet internalPool, les références ne sont pas libérées et donc pas garbage collectible, et cela peut devenir un problème si beaucoup d'objets sont créés.

Cela fonctionne pour la classe String car elle est utilisée de manière intensive et le pool est constitué d'objet "interné" uniquement.

Cela fonctionne également bien pour la classe booléenne, car il n'y a que deux valeurs possibles.

Et enfin c'est aussi la raison pour laquelle valueOf(int) dans la classe Integer est limitée à -128 à 127 valeurs int.

 4
Author: OscarRyz, 2009-07-14 20:57:11

Dans votre premier exemple, vous créez une chaîne, "stupide", puis la passez en paramètre au constructeur de copie d'une autre chaîne, ce qui crée une deuxième chaîne identique à la première. Étant donné que les chaînes Java sont immuables (ce qui pique fréquemment les personnes habituées aux chaînes C), il s'agit d'un gaspillage inutile de ressources. Vous devriez plutôt utiliser le deuxième exemple car il ignore plusieurs étapes inutiles.

Cependant, le littéral de chaîne n'est pas un CaseInsensitiveString donc là, vous ne pouvez pas faire ce que vous voulez dans votre dernier exemple. De plus, il n'y a aucun moyen de surcharger un opérateur de casting comme vous pouvez le faire en C++, il n'y a donc littéralement aucun moyen de faire ce que vous voulez. Vous devez plutôt le passer en tant que paramètre au constructeur de votre classe. Bien sûr, j'utiliserais probablement simplement une chaîne.toLowerCase() et être fait avec elle.

De plus, votre CaseInsensitiveString devrait implémenter l'interface CharSequence ainsi que probablement les interfaces Sérialisables et comparables. De bien sûr, si vous implémentez Comparable, vous devez également remplacer equals() et hashCode ().

 3
Author: James, 2008-12-02 16:29:11

Ce n'est pas parce que vous avez le mot String dans votre classe que vous obtenez toutes les fonctionnalités spéciales de la classe String intégrée.

 3
Author: javaguy, 2010-05-04 21:08:53

CaseInsensitiveString n'est pas un String bien qu'il contient un String. Un String littéral par exemple "exemple" ne peut être attribué qu'à un String.

 3
Author: fastcodejava, 2010-12-14 03:54:52

CaseInsensitiveString et String sont des objets différents. Vous ne pouvez pas faire:

CaseInsensitiveString cis = "Polish";

Parce que "Polish" est une chaîne, pas un CaseInsensitiveString. Si String extended CaseInsensitiveString String alors vous seriez OK, mais évidemment ce n'est pas le cas.

Et ne vous inquiétez pas de la construction ici, vous ne ferez pas d'objets inutiles. Si vous regardez le code du constructeur, tout ce qu'il fait est de stocker une référence à la chaîne que vous avez transmise. Rien de plus est créé.

Dans le cas String s = new String("foobar"), il fait quelque chose de différent. Vous créez d'abord la chaîne littérale "foobar", puis en créez une copie en en construisant une nouvelle chaîne. Il n'est pas nécessaire de créer cette copie.

 2
Author: Herms, 2008-12-02 17:03:46

Quand ils disent d'écrire

String s = "Silly";

Au Lieu de

String s = new String("Silly");

Ils le signifient lors de la création d'un objet String car les deux instructions ci-dessus créent un objet String mais la nouvelle version String() crée deux objets String: l'un dans le tas et l'autre dans le pool de constantes string. Par conséquent, avec plus de mémoire.

Mais quand vous écrivez

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

Vous ne créez pas de chaîne au lieu de créer un objet de classe CaseInsensitiveString. Par conséquent vous devez utiliser le nouveau opérateur.

 2
Author: , 2012-05-19 04:27:39

Si je l'ai bien compris, votre question signifie pourquoi nous ne pouvons pas créer un objet en lui attribuant directement une valeur, ne le limitons pas à un Wrapper de classe String en java.

Pour répondre à cela, je dirais simplement que les langages de programmation purement orientés objet ont des constructions et cela dit, que tous les littéraux lorsqu'ils sont écrits seuls peuvent être directement transformés en un objet du type donné.

Cela signifie précisément que si l'interpréteur voit 3, il sera converti en un Integer object car integer est le type défini pour de tels littéraux.

Si l'interpréteur voit quelque chose entre guillemets simples comme 'a', il créera directement un objet de type character, vous n'avez pas besoin de le spécifier car la langue définit l'objet de type character par défaut pour lui.

De même, si l'interpréteur voit quelque chose dans"", il sera considéré comme un objet de son type par défaut, c'est-à-dire string. Il s'agit d'un code natif fonctionnant en arrière-plan.

Grâce au MIT cours de conférence vidéo 6.00 où j'ai eu l'indice pour cette réponse.

 1
Author: dharam, 2012-05-24 18:12:23

En Java, la syntaxe "text" crée une instance de la classe java.lang.Chaîne. La mission:

String foo = "text";

Est une affectation simple, sans constructeur de copie nécessaire.

MyString bar = "text";

Est illégal quoi que vous fassiez car la classe MyString n'est pas non plus java.lang.Chaîne ou une superclasse de java.lang.Chaîne.

 0
Author: Darron, 2008-12-02 16:22:56

Tout d'abord, vous ne pouvez pas créer une classe qui s'étend à partir de String, car String est une classe finale. Et java gère les chaînes différemment des autres classes, donc seulement avec String, vous pouvez faire

String s = "Polish";

Mais avec votre classe, vous devez invoquer le constructeur. Donc, que le code est très bien.

 0
Author: Lucas Gabriel Sánchez, 2008-12-02 16:23:55

J'ajouterais simplement que Java a Constructeurs de copie...

Eh bien, c'est un constructeur ordinaire avec un objet du même type que l'argument.

 0
Author: PhiLho, 2008-12-02 17:15:14

Dans la plupart des versions du JDK, les deux versions seront les mêmes:

Chaîne s = nouvelle chaîne ("silly");

String s = "Plus idiot";

Parce que les chaînes sont immuables, le compilateur maintient une liste de constantes de chaîne et si vous essayez d'en créer une nouvelle, vérifiez d'abord si la chaîne est déjà définie. Si c'est alors une référence à la chaîne immuable existante est renvoyée.

Pour clarifier - lorsque vous dites " String s =", vous définissez une nouvelle variable qui prend de la place sur la pile - alors que vous disiez "Plus idiot" ou nouvelle chaîne("idiot") exactement la même chose se produit - une nouvelle chaîne constante est compilée dans votre application et les points de référence à cela.

Je ne vois pas la distinction ici. Cependant, pour votre propre classe, qui n'est pas immuable, ce comportement est hors de propos et vous devez appeler votre constructeur.

MISE À JOUR: J'avais tort! Sur la base d'un vote négatif et d'un commentaire ci joint j'ai testé cela et je me rends compte que ma compréhension est faux-new String ("Silly") crée en effet une nouvelle chaîne plutôt que de réutiliser celle existante. Je ne sais pas pourquoi ce serait (quel est l'avantage?) mais le code parle plus fort que les mots!

 0
Author: Ewan Makepeace, 2008-12-04 01:04:11

String est l'une des classes spéciales dans lesquelles vous pouvez les créer sans la nouvelle partie Sring

C'est la même chose que

Int x = y;

Ou

Char c;

 0
Author: Tolga E, 2009-07-14 21:00:12

C'est une loi fondamentale que les chaînes en java sont immuables et sensibles à la casse.

 0
Author: user1945649, 2013-01-06 18:48:43
 String str1 = "foo"; 
 String str2 = "foo"; 

Str1 et str2 appartiennent au même objet String, "foo", b'coz Java gère les chaînes dans StringPool, donc si une nouvelle variable fait référence à la même chaîne, elle n'en crée pas une autre plutôt qu'assigner le même alerady présent dans StringPool.

 String str1 = new String("foo"); 
 String str2 = new String("foo");

Ici, str1 et str2 appartiennent à des Objets différents, b'coz new String() crée avec force un nouvel Objet String.

 0
Author: Akash5288, 2013-12-27 08:29:38

Java crée un objet String pour chaque littéral string que vous utilisez dans votre code. Chaque fois que "" est utilisé, c'est la même chose que d'appeler new String().

Les chaînes sont des données complexes qui "agissent" comme des données primitives. Les littéraux de chaîne sont en fait des objets même si nous prétendons qu'ils sont des littéraux primitifs, comme 6, 6.0, 'c', etc. Ainsi, la chaîne "literal" "text" renvoie un nouvel objet String avec la valeur char[] value = {'t','e','x','t}. Par conséquent, en appelant

new String("text"); 

S'apparente en fait à l'appel de

new String(new String(new char[]{'t','e','x','t'}));

J'espère à partir d'ici, vous pouvez voir pourquoi votre manuel considère cela redondant.

Pour référence, voici l'implémentation de String: http://www.docjar.com/html/api/java/lang/String.java.html

C'est une lecture amusante et pourrait inspirer un aperçu. Il est également idéal pour les débutants de lire et d'essayer de comprendre, car le code démontre un code très professionnel et conforme aux conventions.

Une autre bonne référence est le tutoriel Java sur les chaînes: http://docs.oracle.com/javase/tutorial/java/data/strings.html

 -1
Author: Patrick Michaelsen, 2015-06-23 23:28:55