Classe String en Java
Approfondissement de la classe String en Java : conception, structure interne et méthodes essentielles.
String est le type référence le plus utilisé en Java, et de loin. Vous l'avez rencontré dès le premier jour — String name = "Ada"; — et il s'est glissé dans votre code sans cérémonie. Cette partie du livre creuse sous cette surface. La classe est bien plus riche qu'il n'y paraît : une structure interne fixe, une syntaxe propre au langage que les autres types n'ont pas, un pool mémoire qui influe sur l'identité, et un choix de conception délibéré (l'immutabilité) qui se répercute sur les threads, le hachage et la sécurité.
Ce chapitre est la carte. Le reste de la Partie 9 détaille chaque région.
Qu'est-ce qu'un String ?
Un String est un objet Java ordinaire — java.lang.String, dans le même package que Object et Integer. Il est final, vous ne pouvez donc pas le sous-classer, et chaque méthode qui « modifie » une chaîne renvoie en réalité une nouvelle instance. L'original n'est jamais touché.
String greeting = "hello";
greeting.toUpperCase(); // returns "HELLO" — discarded
System.out.println(greeting); // still prints "hello"
greeting = greeting.toUpperCase();
System.out.println(greeting); // now prints "HELLO"Cette habitude de retourner une nouvelle instance est le fait le plus important à retenir sur cette classe. Elle est approfondie dans immutabilité des String en Java.
Traitement au niveau du langage
Deux éléments de syntaxe sont réservés à String et ne sont pas extensibles aux autres types :
- Les littéraux de chaîne —
"hello"produit un objetStringdirectement, sansnew. Le compilateur déduplique également les littéraux identiques dans le pool de chaînes. - L'opérateur
+— surchargé pour les chaînes :"a" + "b"donne"ab". Même les expressions mixtes comme"score: " + 42fonctionnent, car Java convertit le membre droit enString.
En coulisses, les compilateurs Java modernes traduisent les chaînes de + en utilisant StringBuilder ou la StringConcatFactory basée sur invokedynamic, vous n'avez donc presque jamais besoin d'écrire la concaténation manuellement. Le compilateur sait quoi faire.
Structure interne
Avant Java 9, chaque String contenait un char[] — deux octets par caractère quel que soit le contenu. Java 9 a introduit les compact strings : le tableau de support est désormais un byte[], plus un champ coder d'un octet qui indique si les octets sont Latin-1 (un octet par caractère) ou UTF-16 (deux octets). Pour le texte qui tient en Latin-1 — la plupart des codes, configurations, identifiants, anglais courant — cela réduit à peu près de moitié l'empreinte mémoire sans changer l'API.
Vous ne pouvez pas voir ce champ, vous ne pouvez pas le modifier, et vous n'avez pas à y penser. Mais c'est pourquoi les programmes à forte utilisation de chaînes sous JDK 9+ consomment nettement moins de tas qu'avec JDK 8.
Les familles de méthodes principales
L'API de String est vaste mais s'organise en un petit nombre de groupes reconnaissables :
Inspection. length(), isEmpty(), isBlank(), charAt(i), codePointAt(i), hashCode().
Recherche. indexOf, lastIndexOf, contains, startsWith, endsWith, matches.
Extraction. substring(start), substring(start, end), chars(), codePoints(), toCharArray().
Transformation. toUpperCase(), toLowerCase(), trim(), strip(), replace, replaceAll, replaceFirst, concat.
Découpage et jonction. split, String.join — traités dans split() et join().
Formatage. String.format, la méthode d'instance formatted, et la sortie de style printf — traités dans formatage de String.
Comparaison. equals, equalsIgnoreCase, compareTo, compareToIgnoreCase, contentEquals — traités dans comparaison de String.
Conversion. valueOf (statique), toString (d'instance), les assistants d'analyse sur Integer, Double, etc. — traités dans conversions de String.
Les listes complètes de l'API se trouvent dans la Javadoc du JDK. La compétence clé est de reconnaître quelle famille utiliser, pas de mémoriser chaque surcharge.
Les String sont des séquences d'unités de code UTF-16
charAt(i) et length() comptent les unités de code UTF-16, pas les caractères Unicode. Pour le texte dans le Plan multilingue de base (la grande majorité des scripts courants), un char = un caractère et la distinction n'a aucune importance. Pour les caractères supplémentaires — la plupart des emoji, certaines extensions CJK, les scripts anciens — un caractère visible occupe deux chars, une paire de substitution.
String emoji = "🙂";
System.out.println(emoji.length()); // 2 — two code units
System.out.println(emoji.codePointCount(0, emoji.length())); // 1 — one code pointSi vous avez besoin d'itérer par point de code Unicode, utilisez codePoints() ou codePointAt. Pour la plupart des cas d'utilisation de type ASCII — découpage de CSV, formatage de lignes de log, comparaison d'identifiants — length() et charAt sont exactement ce qu'il vous faut.
Les cousins mutables : StringBuilder et StringBuffer
Lorsque vous devez construire une chaîne morceau par morceau, les += répétés allouent un nouveau String à chaque étape. La bibliothèque standard propose deux compagnons mutables pour ce cas :
StringBuilder— rapide, mono-thread.StringBuffer— même API, méthodes synchronisées, utile uniquement lorsque plusieurs threads écrivent dans le même tampon.
Ils ont des API parallèles et des chapitres parallèles dans cette partie du livre.
Un exemple pratique
Un petit exercice qui touche aux familles les plus courantes — inspection, recherche, extraction, transformation et conversion — sur la même entrée. Lisez la sortie ligne par ligne ; chaque appel illustre un outil de la liste ci-dessus.
La dernière ligne est la conclusion. Après toutes les transformations que nous avons appliquées, line lui-même est identique octet pour octet au littéral avec lequel nous avons commencé — la preuve que le modèle « retourner une nouvelle instance » est bien réel, et pas seulement une note de documentation.
La suite
Les String disposent d'un modèle mémoire unique dans la bibliothèque standard : les littéraux identiques partagent le même espace de stockage, et vous pouvez inclure n'importe quelle chaîne dans ce pool partagé avec un seul appel de méthode. Continuez vers le pool de String en Java.