IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

 

Développons en Java   2.30  
Copyright (C) 1999-2022 Jean-Michel DOUDOUX    (date de publication : 15/06/2022)

[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

 

6. Les chaînes de caractères

 

chapitre    6

 

Niveau : niveau 1 Fondamental 

 

Dans de nombreux langages, les chaînes de caractères sont représentées et manipulées sous la forme de tableau de caractères. Dans les langages plus récents comme Java, les chaînes de caractères sont encapsulées et manipulées dans des objets.

En Java, une chaîne de caractères est encapsulée de manière immuable dans une instance de type java.lang.String.

Comme la classe String est fréquemment utilisée, le langage Java propose quelques simplifications pour faciliter leur manipulation, notamment :

  • la définition d'une instance sans utiliser l'opérateur new mais en utilisant directement la syntaxe littérale
  • la possibilité de concaténer des chaînes en utilisant l'opérateur +
Exemple :
String texte = "Bonjour";

Même avec une syntaxe similaire à celle utilisée pour définir de variables primitives, les variables de type String sont des objets. Partout où des constantes chaînes de caractères figurent entre guillemets, le compilateur Java génère un objet de type String avec le contenu spécifié. Il est donc possible d'écrire :

Exemple :
   String texte = "Java Java Java".replace('a','o');

Il est impossible de modifier le contenu d'un objet de type String. Cependant, il est possible d'utiliser les méthodes de la classe String pour effectuer une modification qui va créer une nouvelle instance de type String encapsulant la nouvelle chaîne de caractères.

Il est donc important d'utiliser la chaîne retournée par ces méthodes. Il est par exemple possible d'affecter le résultat de ces méthodes à la variable pour qu'elle pointe sur la nouvelle chaîne de caractères contenant les modifications ou sur une autre variable pour pouvoir conserver une référence sur l'originale.

Exemple :
String texte = "Java Java Java";
texte.replace('a','o');
System.out.println(texte) ;
texte = texte.replace('a','o');
System.out.println(texte) ;
Résultat :
Java Java Java
Jovo Jovo Jovo

Plusieurs classes implémentant des interfaces sont proposées pour manipuler des chaînes de caractères :

  • java.lang.String : encapsule une chaîne de caractères de manière immuable
  • java.lang.StringBuffer : concaténer des chaînes de manière mutable (thread safe)
  • java.lang.StringBuilder : concaténer des chaînes de manière mutable (non thread safe)
  • java.util.StringTokenizer : séparation d'une chaîne selon un séparateur
  • java.util.StringJoiner : concaténer des chaînes avec un séparateur et éventuellement un préfixe et un suffixe

Diagramme de classes String

Ce chapitre contient plusieurs sections :

 

6.1. Les chaînes de caractères littérales

Historiquement, la syntaxe du langage Java propose des chaînes de caractères littérales mono-ligne.

Depuis Java 15, la syntaxe de Java propose les blocs de texte qui permettent de facilement exprimer des chaînes de caractères littérales multilignes.

 

6.1.1. Les chaînes de caractères littérales mono ligne

La syntaxe pour définir une chaîne de caractères littérale utilise comme séparateur des doubles quotes.

Il est possible de :

  • définir une variable de type java.lang.String
  • de l'initialiser en fournissant la chaîne directement après l'opérateur =
  • fournir la valeur de la chaîne en paramètre d'un objet de type String
Exemple :
    String libelle = "";
    System.out.println("test");

 

6.1.2. Les séquences d'échappement

Dans une chaîne de caractères littérale, certains caractères doivent être échappés en utilisant une séquence d'échappement. Une séquence d'échappement débute par le caractère d'échappement qui est le caractère backslash \.

Exemple :
    System.out.println("\"Bonjour\"");

Evidemment, le caractère backslash doit lui-même être échappé pour être inclus dans une chaîne de caractères littérale.

Exemple :
    System.out.println("c:\\temp");

Dans une chaîne de caractères, plusieurs caractères particuliers doivent être utilisés avec le caractère d'échappement \. Le tableau ci-dessous recense les principaux caractères définis dans les spécifications Java  :

Caractères spéciaux

Affichage

Unicode

\'

Apostrophe

 

\"

Double quote

 

\'

Quote

\u0027

\\

Backslash

\u005c

\t

Tabulation

\u0009

\b

Retour arrière (backspace BS)

\u0008

\r

Retour chariot (carriage return CR)

\u000d

\f

Saut de page (form feed FF)

\u000c

\n

Saut de ligne (line feed LF)

\u000a

\0ddd

Caractère ASCII ddd (octal)

 

\xdd

Caractère ASCII dd (hexadécimal)

 

\udddd

Caractère Unicode dddd (hexadécimal)

 


Il est aussi possible d'utiliser des séquences d'échappement octale ou hexadécimale pour les caractères ASCII et Unicode.

Chaque caractère est associé à une valeur entière en fonction de l'encodage de caractère utilisé pour le représenter.

Exemple avec le caractère Euro :

Encoding

Valeur(s) hexadécimale(s)

ASCII

Impossible

ISO 8859-15

0xA4

CP 1252

0x80

UTF-8

0xE2 0x82 0xA0

UTF-16

0x20A0

UTF-32

0x000020A0


Pour les caractères ASCII, donc sur un seul octet, une séquence d'échappement permet de fournir la valeur du caractère en octal (0 à 377) ou en hexadécimal (0 à FF).

Il est possible de représenter un caractère en utilisant son code Unicode dans une séquence d'échappement dédiée. Cette séquence est composée de :

  • \u
  • suivi du code Unicode du caractère en hexadécimal sur 4 chiffres
Exemple :
    System.out.println("10 \u20AC");
Résultat :
10 €

Attention : les séquences d'échappement Unicode ne supportent que des valeurs comprises entre 0000 et FFFF (65635).

Il y a deux possibilités pour palier à cette limitation :

  • fournir les deux valeurs hexadécimales du code en UTF-16
  • depuis Java 1.5, utiliser la méthode toChars() de la classe Character qui attend en paramètre un entier correspond au code Unicode
Exemple ( code Java 5.0 ) :
    int ch = 0x1F4A9; // caractère unicode emoji tas de crotte

    String s = new String(Character.toChars(ch));
    System.out.println(s);

    System.out.println("\uD83D\uDCA9");

 

6.2. La classe java.lang.String

Les chaînes de caractères sont encapsulées de manière immuable dans des instances de la classe java.lang.String.

 

6.2.1. Les constructeurs

La classe String possède plusieurs constructeurs :

Constructeur

Description

String()

Obtenir une nouvelle instance qui encapsule une chaîne vide

String​(byte[] bytes)

Depuis Java 1.1, obtenir une nouvelle instance en décodant le tableau d'octets avec le charset par défaut du système

String​(byte[] ascii, int hibyte)

Déprécié depuis Java 1.1 car elle ne convertit pas correctement les octets en caractères

String​(byte[] bytes, int offset, int length)

Depuis Java 1.1, obtenir une nouvelle instance en décodant les octets du sous-tableau avec le charset par défaut de la plateforme

String​(byte[] ascii, int hibyte, int offset, int count)

Déprécié depuis Java 1.1 car elle ne convertit pas correctement les octets en caractères

String​(byte[] bytes, int offset, int length, String charsetName)

Depuis Java 1.1, obtenir une nouvelle instance en décodant le sous-tableau d'octets avec le charset dont le nom est précisé

String​(byte[] bytes, int offset, int length, Charset charset)

Obtenir une nouvelle instance en décodant le sous-tableau d'octets avec le charset précisé

String​(byte[] bytes, String charsetName)

Depuis Java 1.1, obtenir une nouvelle instance en décodant le tableau d'octets avec le charset dont le nom est précisé

String​(byte[] bytes, Charset charset)

Depuis Java 1.6, obtenir une nouvelle instance en décodant le tableau d'octets avec le charset précisé

String​(char[] value)

Obtenir une nouvelle instance encapsulant le tableau de caractères

Exemple :
      char chars[] = { 't', 'e', 's', 't' };
      String chaine = new String(chars);
  

String​(char[] value, int offset, int count)

Obtenir une nouvelle instance encapsulant le sous-tableau de caractères dont le premier caractère est indiqué avec le paramètre offset et le nombre de caractères à inclure est indiqué avec le paramètre count

Exemple :
      char chars[] = { 't', 'e', 's', 't' };
      String chaine = new String(chars,1,2);
  

String​(int[] codePoints, int offset, int count)

Depuis Java 1.5, obtenir une nouvelle instance en décodant le sous-tableau de codepoints Unicode avec le charset dont le nom est précisé

String​(String original)

Obtenir une nouvelle instance encapsulant les caractères de la chaîne passée en paramètre (faire une copie de la chaîne)

Exemple :
      String chaine = new String("test");
  

String​(StringBuffer buffer)

Obtenir une nouvelle instance encapsulant les caractères contenus dans le StringBuffer

String​(StringBuilder builder)

Depuis Java 1.5, obtenir une nouvelle instance encapsulant les caractères contenus dans le StringBuilder


 

6.2.2. Les méthodes

La classe String possède de nombreuses méthodes pour manipuler la chaîne de caractères qu'elle encapsule dont voici les principales :

Méthode

Rôle

char charAt​(int index)

Renvoyer le caractère à la position fournie en paramètre

IntStream chars()

Depuis Java 9, renvoyer un IntStream des valeurs entières des caractères de la chaîne

int codePointAt​(int index)

Depuis Java 1.5, renvoyer le code point Unicode du caractère à l'index précisé

int codePointBefore​(int index)

Depuis Java 1.5, renvoyer le code point Unicode du caractère précédent l'index précisé

int codePointCount​(int beginIndex, int endIndex)

Depuis Java 1.5, renvoyer le nombre de code point Unicode entre les index fournis en paramètre

IntStream codePoints()

Depuis Java 9, renvoyer un Stream des code points Unicode des caractères de la chaîne

int compareTo​(String anotherString)

Comparer la chaîne avec celle fournie de manière lexicographique

int compareToIgnoreCase​(String str)

Depuis Java 1.2, comparer la chaîne avec celle fournie de manière lexicographique sans tenir compte de la casse

String concat​(String str)

Concaténer la chaîne avec celle fournie en paramètre

boolean contains​(CharSequence s)

Depuis Java 1.5, renvoyer un booléen qui indique si la chaîne contient la séquence de caractères fournie en paramètre

boolean contentEquals​(CharSequence cs)

Depuis Java 1.5, comparer la chaîne avec la séquence de caractères fournie en paramètre

boolean contentEquals​(StringBuffer sb)

Depuis Java 1.4, comparer la chaîne avec celle contenue dans le StringBuilder fourni en paramètre

static String copyValueOf​(char[] data)

Equivalent à valueOf(char[])

static String copyValueOf​(char[] data, int offset, int count)

Equivalent à valueOf(char[], int, int)

boolean endsWith​(String suffix)

Tester si la chaîne se termine par celle fournie en paramètre

boolean equals​(Object anObject)

Comparer la chaîne avec celle fournie en paramètre en tenant compte de la casse

boolean equalsIgnoreCase​(String anotherString)

Comparer la chaîne avec celle fournie en paramètre sans tenir compte de la casse

static String format​(String format, Object... args)

Depuis Java 1.5, renvoyer une chaîne formatée en utilisant la chaîne de format et les arguments fournis

static String format​(Locale l, String format, Object... args)

Depuis Java 1.5, renvoyer une chaîne formatée en utilisant la Locale, la chaîne de format et les arguments fournis

String formatted​(Object... args)

Depuis Java 15, renvoyer une chaîne formatée en utilisant la chaîne comme format et les arguments fournis

byte[] getBytes()

Depuis Java 1.1, renvoyer un tableau des octets des caractères de la chaîne encodé avec le charset par défaut du système

void getBytes​(int srcBegin, int srcEnd, byte[] dst, int dstBegin)

Dépréciée depuis Java 1.1 car elle ne convertit pas correctement les caractères en octets

byte[] getBytes​(String charsetName)

Depuis Java 1.1, renvoyer un tableau des octets des caractères de la chaîne encodé avec le charset dont le nom est précisé

byte[] getBytes​(Charset charset)

Depuis Java 1.6, renvoyer un tableau des octets des caractères de la chaîne encodé avec le charset précisé

void getChars​(int srcBegin, int srcEnd, char[] dst, int dstBegin)

Copier les caractères de la chaîne entre les positions précisées dans le tableau fourni en paramètre

int hashCode()

Renvoyer la valeur de hachage de la chaîne

String indent​(int n)

Depuis Java 11, ajouter une indentation de n espaces précisés en paramètre et normaliser les caractères de terminaison

int indexOf​(int ch)

Renvoyer l'index de la première occurrence du caractère fourni en paramètre dans la chaîne

int indexOf​(int ch, int fromIndex)

Retourner l'index dans la chaîne de la première occurrence du caractère en commençant la recherche à partir de l'index précisé

int indexOf​(String str)

Renvoyer la position de la première occurrence de la chaîne fournie en paramètre si l'instance la contient, sinon elle renvoie -1

int indexOf​(String str, int fromIndex)

Retourner l'index dans la chaîne de la première occurrence de la chaîne fournie en commençant la recherche à partir de l'index précisé

String intern()

Retourner une représentation canonique de la chaîne.
Elle vérifie si une instance de String est égale à une qui se trouve dans le pool. Si elle est présente, alors l'instance de String du pool est renvoyée. Sinon, l'instance de String est ajoutée au pool et la référence à cette instance est renvoyée. Par conséquent, pour deux chaînes de caractères s1 et s2, s1.intern() == s2.intern() est vrai si et seulement si s1.equals(s2) est vrai

boolean isBlank()

Depuis Java 11, renvoyer un booléen qui précise si la chaîne est vide ou ne contient que des caractères considérés comme des espaces

boolean isEmpty()

Depuis Java 1.6, renvoyer un booléen qui indique si la chaîne ne contient aucun caractère : donc true si length() renvoie 0

static String join​(CharSequence delimiter, CharSequence... elements)

Depuis Java 1.8, renvoyer une chaîne composée de copies des éléments fournis concaténés avec le délimiteur

 

static String join​(CharSequence delimiter, Iterable<? extends CharSequence> elements)

Depuis Java 1.8, renvoyer une chaîne composée de copies des éléments fournis concaténés avec le délimiteur

 

int lastIndexOf​(int ch)

Renvoyer l'index de la dernière occurrence dans la chaîne du caractère précisé

int lastIndexOf​(int ch, int fromIndex)

Renvoyer l'index de la dernière occurrence dans la chaîne du caractère précisé, la recherche commençant en reculant à l'index fourni

int lastIndexOf​(String str)

Renvoyer l'index de la dernière occurrence dans la chaîne de la chaîne précisée

int lastIndexOf​(String str, int fromIndex)

Renvoyer l'index dans la chaîne de la dernière occurrence de la sous-chaîne spécifiée, la recherche commençant en reculant à l'index fourni

int length()

Renvoyer le nombre de caractères de la chaîne

Stream<String> lines()

Depuis Java 11, renvoyer un Stream dont les éléments sont les lignes de la chaîne

boolean matches​(String regex)

Depuis Java 1.4, renvoyer un booléen qui précise si la chaîne respecte l'expression régulière fournie en paramètre

boolean regionMatches​(boolean ignoreCase, int toffset, String other, int ooffset, int len)

Tester si une portion de la chaîne est égale à celle d'une autre chaîne fournie en paramètre

boolean regionMatches​(int toffset, String other, int ooffset, int len)

Tester si une portion de la chaîne est égale à celle d'une autre chaîne fournie en paramètre

String repeat​(int count)

Depuis Java 11, renvoyer une chaîne qui est la concaténation d'elle-même le nombre de fois fournie en paramètre

String replace​(char oldChar, char newChar)

Depuis Java 1.5, renvoyer une chaîne dont tous les caractères oldChar sont remplacés par newChar

String replace​(CharSequence target, CharSequence replacement)

Renvoyer une chaîne dont laquelle toutes les sous-chaînes précisées dans target sont remplacées par celles précisées par replacement

String replaceAll​(String regex, String replacement)

Depuis Java 1.4, renvoyer une chaîne dont toutes les portions qui respectent l'expression régulière sont remplacées par la chaîne fournie

String replaceFirst​(String regex, String replacement)

Depuis Java 1.4, renvoyer une chaîne dans laquelle la première sous-chaîne qui correspond à l'expression régulière fournie est remplacée par la valeur fournie dans replacement

String[] split​(String regex)

Depuis Java 1.4, découper la chaîne selon le séparateur fourni en paramètre sous la forme d'une expression régulière

String[] split​(String regex, int limit)

Depuis Java 1.4, renvoyer un tableau de chaîne qui contient le découpage de la chaîne selon l'expression régulière fournie

boolean startsWith​(String prefix)

Renvoyer un booléen qui indique si la chaîne commence par celle fournie en paramètre

boolean startsWith​(String prefix, int toffset)

Tester si la sous-chaîne de cette chaîne commençant à l'index spécifié commence par le préfixe fourni

String strip()

Depuis Java 11, renvoyer une chaîne dont les espaces de début et de fin sont retirés

String stripIndent()

Depuis Java 15, renvoyer une chaîne dont les indentations accessoires en début et fin de lignes dans une chaîne de caractères sont retirées

String stripLeading()

Depuis Java 11, renvoyer une chaîne dont la valeur est cette chaîne, avec tous les espaces blancs en début de chaîne dont supprimés

String stripTrailing()

Depuis Java 11, renvoyer une chaîne dont la valeur est cette chaîne, avec tous les espaces blancs en fin de chaîne sont supprimés

CharSequence subSequence​(int beginIndex, int endIndex)

Depuis Java 1.4, renvoyer une sous-chaîne correspondant aux index de début et de fin fournis

String substring​(int beginIndex)

Renvoyer une chaîne qui contient les caractères de la chaîne à partir de la position fournie en paramètre

String substring​(int beginIndex, int endIndex)

Renvoyer une chaîne qui contient les caractères de la chaîne à entre les positions de début et de fin fournies en paramètres

char[] toCharArray()

Renvoyer un tableau des caractères de la chaîne

String toLowerCase()

Renvoyer une nouvelle chaîne qui contient l'ensemble des caractères en minuscules

String toLowerCase​(Locale locale)

Depuis Java 1.1, renvoyer une nouvelle chaîne qui contient l'ensemble des caractères en minuscules en tenant de la Locale fournie

String toString()

Renvoie l'instance elle-même

String toUpperCase()

Renvoyer une nouvelle chaîne qui contient l'ensemble des caractères en majuscules

String toUpperCase​(Locale locale)

Depuis Java 1.1, renvoyer une nouvelle chaîne qui contient l'ensemble des caractères en majuscules en tenant de la Locale fournie

<R> R transform​(Function<? super String,​? extends R> f)

Depuis Java 12, renvoyer le résultat de l'application de la Function fournie en paramètre à la chaîne de caractères

String translateEscapes()

Depuis Java 15, renvoyer une chaîne dont la valeur est cette chaîne avec ses séquences d'échappement traduites comme dans une chaîne littérale

String trim()

Retourner une chaîne de caractères dont les caractères non significatifs ayant un code inférieur ou égal à "U+0020" de début et de fin sont retirés

static String valueOf​(boolean b)

Retourner la représentation sous la forme d'une chaîne de caractères du booléen fourni en paramètre

static String valueOf​(char c)

Retourner la représentation sous la forme d'une chaîne de caractères du caractère fourni en paramètre

static String valueOf​(char[] data)

Retourner la représentation sous la forme d'une chaîne de caractères du tableau de caractères fourni en paramètre

static String valueOf​(char[] data, int offset, int count)

Retourner la représentation sous la forme d'une chaîne de caractères du sous-tableau de caractères obtenu avec les paramètres fournis

static String valueOf​(double d)

Renvoyer une représentation textuelle de la valeur double fournie en paramètre

static String valueOf​(float f)

Renvoyer une représentation textuelle de la valeur float fournie en paramètre

static String valueOf​(int i)

Renvoyer une représentation textuelle de la valeur entière fournie en paramètre

static String valueOf​(long l)

Renvoyer une représentation textuelle de la valeur entière fournie en paramètre

static String valueOf​(Object obj)

Renvoyer une représentation textuelle de l'objet passé en paramètre


Comme la classe String encapsule une chaîne de caractères de manière immutable, toutes les méthodes de modification retourne une nouvelle instance qui encapsule le résultat de la modification.

 

6.2.3. La classe String est immuable

Une chaîne de caractères en Java, encapsulée dans une instance de type java.lang.String est immutable : elle ne peut pas être modifiée. Le résultat d'une action de modification créé une nouvelle chaîne qui contient le résultat de la modification et laisse la chaîne initiale dans le même état.

Chaque traitement qui vise à transformer une instance de la classe est implémentée par une méthode qui laisse l'objet d'origine inchangé et renvoie une nouvelle instance de String contenant les modifications.

Exemple :
private String uneChaine;

void miseEnMajuscule(String chaine) {
  uneChaine = chaine.toUpperCase()
}

Il est ainsi possible d'enchaîner plusieurs méthodes :

Exemple :
uneChaine = chaine.toUpperCase().trim();

Les chaînes de caractères sont immuables pour améliorer la performance et la sécurité.

L'immutabilité des chaînes de caractères apporte plusieurs bénéfices :

  • elles sont de fait thread-safe
  • leur hashcode peut être mis en cache
  • permet de réutiliser des chaînes identiques en utilisant un pool de Strings

 

6.2.4. L'encapsulation des caractères des chaînes de caractères

Les chaînes de caractères ne sont pas des tableaux que l'on peut manipuler directement car les caractères sont encapsulés dans la classe String : il faut utiliser les méthodes de la classe String sur une instance pour effectuer des manipulations.

Historiquement, Java ne fonctionne pas avec le jeu de caractères ASCII ou ANSI, mais avec Unicode (Universal Code). Ceci concerne les types char et les chaînes de caractères.

Jusqu'à Java 8, la classe String encapsule une chaîne de caractères en utilisant en interne un tableau de char, dans lequel chaque caractère est encodé en UTF-16. Cela implique que chaque caractère requiert deux octets même si ce caractère est dans la table ASCII.

A partir de Java 9, une nouvelle implémentation de la classe String est proposée sous le nom de Compact Strings. Selon les caractères contenus, les caractères sont stockés dans un tableau de char (UTF-16) ou un tableau de byte (Latin-1).

Si les caractères sont tous dans la table ASCII, ils pourront être stockés sur un seul octet. Ainsi, si tous les caractères sont dans la table ASCII, on peut réduire la taille en mémoire requise pour stocker les chaînes de caractères.

 

6.3. La création d'un objet de type String

La syntaxe du langage Java propose deux possibilités pour obtenir une référence vers un objet de type java.lang.String.

 

6.3.1. L'utilisation de la syntaxe littérale

Le plus simple pour définir une instance de type String est d'utiliser la syntaxe littérale.

Exemple :
    String chaine = "Bonjour";

Cette syntaxe littérale permet de définir une instance de type String sans avoir à utiliser l'instruction new comme pour tous les autres types.

Il est aussi possible d'utiliser une expression qui n'utilise que la syntaxe littérale.

Exemple :
    String chaine = "Bon"+"jour";

 

6.3.2. L'utilisation d'un constructeur

Il est aussi possible d'invoquer un des constructeurs de la classe String.

Exemple :
    char[] chars = { 'B', 'o', 'n', 'j', 'o', 'u', 'r' };
    String chaine = new String(chars);

 

6.3.3. Syntaxe littérale vs utilisation d'un constructeur

Si on utilise la syntaxe littérale, on obtient la référence d'une nouvelle instance si la chaîne n'est pas présente dans le pool et elle y est ajoutée, sinon on obtient la référence de chaîne présente dans le pool.

Exemple :
    String chaine1 = "Bonjour";
    String chaine2 = "Bonjour";
    System.out.println(chaine1 == chaine2);
Résultat :
true

Lorsque que l'on créé une instance de type String en utilisant l'instructeur new, un nouvel objet est systématiquement créé et stocké dans le heap.

Exemple :
    String chaine1 = "Bonjour";
    String chaine2 = new String("Bonjour");
    System.out.println(chaine1 == chaine2);
Résultat :
false

La création d'une chaîne en utilisant un des constructeurs crée systématiquement une nouvelle instance.

L'utilisation de la syntaxe littérale pour créer une chaîne implique toujours que celle-ci soit gérée dans le pool de chaînes.

Il est donc recommandé d'utiliser la syntaxe littérale plutôt que l'opérateur new pour créer des instances de type String lorsque que l'on connaît déjà sa valeur.

 

6.4. Les opérations sur les chaînes de caractères

La classe String possède de nombreuses méthodes pour effectuer des traitements sur la chaîne qu'elle encapsule et/ou sur d'autres chaînes.

 

6.4.1. Le test de l'égalité de deux chaînes de caractères

Comme c'est le cas dans d'autres langages, il est tentant de tester l'égalité de deux chaînes avec l'opérateur ==.

Ce test est valide, il teste l'égalité sur les références des deux instances. De par le fonctionnement en interne dans la JVM et de la manière dont les chaînes sont définies, ce test peut renvoyer true ou false.

Il est préférable d'utiliser la méthode equals() pour tester l'égalité de la chaîne de caractères avec celle fournie en paramètre.

La classe String redéfinit la méthode equals() héritée de la classe Objet pour retourner true si le paramètre n'est pas null et si c'est un objet de type String qui encapsule une chaîne ayant la même séquence de caractères en tenant compte de la casse.

Exemple :
String texte1 = "texte 1";
String texte2 = "texte 2";
if ( texte1.equals(texte2) ) {
  // les deux chaînes sont égales

  // ...

}

Le test réalisé par la méthode equals() est sensible à la casse. Pour ne pas tenir compte de la casse, il faut utiliser la méthode equalsIgnoreCase().

La méthode equalsIgnoreCase() compare la chaîne avec celle fournie en paramètre en ne tenant pas compte de la casse. Elle renvoie true si les deux chaînes de caractères ont la même taille et contiennent la même séquence de caractères sans tenir compte de la casse.

Exemple :
String texte1 = "texte";
String texte2 = "TEXTE";              
System.out.println(texte1.equals(texte2));
System.out.println(texte1.equalsIgnoreCase(texte2));
Résultat :
false
true

Les méthodes equals() et equalsIgnoreCase() permettent d'effectuer une vérification lexicographique de l'égalité de deux chaînes.

L'utilisation de l'opérateur == effectue une vérification sur les références. Il n'est pas recommandé d'utiliser l'opérateur == car celui-ci teste l'égalité des références des deux chaînes.

Exemple :
String texte1a = "texte 1";
String texte1b = "texte 1";           
String texte2a = "texte 2";
String texte2b = new String("texte 2");
System.out.println(texte1a == texte1b);
System.out.println(texte1a == texte2a);
System.out.println(texte2a == texte2b);
Résultat :
true
false
false

A quelques rares exceptions près, la comparaison de deux chaînes de caractères doit se faire avec la méthode equals().

Il est aussi possible de comparer la chaîne de caractères avec une autre en utilisant les méthodes compareTo() et compareToIgnoreCase(). Celles-ci retournent 0 si les deux chaînes sont égales, une valeur négative si la chaîne est plus petite que celle fournie et une valeur positive si la chaîne est plus grande que celle fournie.

Exemple :
    String texte1 = "texte";
    String texte2 = "texte";
    String texte3 = "TEXTE";
    System.out.println(texte1.compareTo(texte2));
    System.out.println(texte1.compareToIgnoreCase(texte3));
Résultat :
0
0

 

6.4.1.1. equals() vs ==

La méthode equals() compare le contenu de la chaîne avec celle fournie en paramètre.

L'opérateur == teste l'égalité des références : si les deux variables de type String pointe sur la même référence alors l'opérateur renvoie true. Si les références sont différentes alors il renvoie false.

Exemple :
    String s0 = "Te";
    String s1 = "Test";                   // Chaîne littérale

    String s2 = "Te"+"st";                // Chaîne littérale

    String s3 = s1;                       // affectation de la référence

    String s4 = new String("Test");       // nouvelle instance

    String s5 = new String("Test");       // nouvelle instance

    String s6 = s0 + "st";                // Chaîne non littérale    

 
    System.out.println(s1 == s1);         // true, même référence

    System.out.println(s1 == s2);         // true, même référence, celle du pool de String

    System.out.println(s1 == s3);         // true, même référence

    System.out.println(s1 == s4);         // false, références différentes

    System.out.println(s4 == s5);         // false, références différentes

    System.out.println(s1 == s6);         // false, références différentes

     
    System.out.println(s1.equals(s2));    // true, contenu identique

    System.out.println(s1.equals(s3));    // true, contenu identique

    System.out.println(s1.equals(s4));    // true, contenu identique

    System.out.println(s4.equals(s5));    // true, contenu identique

    System.out.println(s1.equals(s6));    // true, contenu identique

Résultat :
true
true
true
false
false
false
true
true
true
true
true

 

6.4.2. La comparaison de chaînes de caractère

Plusieurs méthodes de classe String peuvent être utilisées pour comparer tout ou partie d'une chaîne de caractères avec une autre.

La méthode compareTo() est une redéfinition de la méthode définie dans l'interface Comparable qui effectue une comparaison lexicographique des codepoints Unicode de chaque caractères avec ceux de la chaîne fournie en paramètre.

Elle renvoie une valeur entière qui peut être :

  • un entier négatif si la chaîne précède lexicographiquement la chaîne passée en paramètre
  • zéro si les deux chaînes sont égales
  • un entier positif si la chaîne suit lexicographiquement la chaîne passée en paramètre

Dans l'ordre lexicographique, deux chaînes sont différentes si :

  • leurs tailles sont différentes
  • ou leurs caractères à au moins un index sont différents

Depuis Java 1.2, la méthode compareToIgnoreCase() effectue une comparaison lexicographique de chaque caractères avec ceux de la chaîne fournie en paramètre sans tenir compte de la casse.

Elle retourne un entier dont la valeur dépend du résultat de la comparaison de la chaîne avec celle fournie en paramètre :

  • un entier négatif si la chaîne précède lexicographiquement la chaîne passée en paramètre
  • zéro si les deux chaînes sont égales
  • un entier positif si la chaîne suit lexicographiquement la chaîne passée en paramètre
Exemple ( code Java 1.2 ) :
    String texte1 = "test";
    String texte2 = "tester";
    String texte3 = "Test";
    System.out.println(texte1.compareTo(texte2));
    System.out.println(texte1.compareTo(texte3));
    System.out.println(texte1.compareToIgnoreCase(texte3));
Résultat :
-2
32
0

Ces méthodes ne prennent pas en compte de Locale. La classe java.text.Collator permet de faire des comparaisons de chaînes en prenant en compte des spécificités liées à une Locale.

La classe Collator est abstraite : les sous-classes mettent en oeuvre des stratégies. La sous-classe RuleBasedCollator fournie par Java est utilisable avec de nombreux langages. D'autres sous-classes peuvent être créées pour répondre à des besoins plus spécifiques. La sous-classe RuleBasedCollator hérite de Collator et implémente l'interface Comparator.

Les surcharges de la méthode getInstance() permettent d'obtenir une instance de type Collator :

  • getInstance() : sans paramètre, elle renvoie une instance pour la Locale par défaut
  • getInstance(Locale) : elle renvoie une instance pour la Locale fournie en paramètre

L'exemple ci-dessous compare deux chaînes de caractères en utilisant un Collator pour la Locale par défaut.

Exemple :
    Collator collator = Collator.getInstance();
    System.out.println(collator.compare("test", "TEST"));
Résultat :
-1

L'exemple ci-dessous compare deux chaînes de caractères en utilisant un Collator pour la Locale US.

Exemple :
    Collator collator = Collator.getInstance(Locale.US);
    System.out.println(collator.compare("test", "TEST"));
Résultat :
-1

La propriété strength détermine le niveau minimum de différence considéré comme significatif lors de la comparaison. Quatre constantes sont utilisables : PRIMARY, SECONDARY, TERTIARY et IDENTICAL. Leur rôle dépend de la Locale et peut par exemple prendre en compte ou non la classe et les caractères accentués.

L'exemple ci-dessous compare deux chaînes de caractères en utilisant un Collator pour la Locale US sans tenir compte de la casse.

Exemple :
    Collator collator = Collator.getInstance(Locale.US);
    collator.setStrength(Collator.PRIMARY);
    System.out.println(collator.compare("test", "TEST"));
Résultat :
0

La méthode endsWith() de la classe String permet de tester si une chaîne se termine par le suffixe fourni en paramètre. Elle renvoie true si la chaîne se termine par le suffixe ou si le suffixe est une chaîne vide ou si le suffixe est égal à la chaîne.

Exemple :
    System.out.println("nom test".endsWith("test"));
    System.out.println("nom test".endsWith(""));
    System.out.println("nom test".endsWith("nom test"));
Résultat :
true
true
true

 

6.4.3. Le formatage d'une chaîne de caractères

Depuis java 1.5, la méthode statique format() de la classe String permet de formater une chaîne de caractères sur la base du format fourni en premier paramètre et des valeurs fournis dans un varargs en paramètres.

Exemple ( code Java 5.0 ) :
    String nom = "JM";
    String salutation = String.format("Bonjour %s", nom);
    System.out.println(salutation);
Résultat :
Bonjour JM

Par défaut, la méthode format() utilise la Locale par défaut du système pour formater certaines données notamment numériques et temporelles.

Depuis Java 1.5, une surcharge de la méthode format() attend en premier paramètre la Locale à utiliser.

Exemple ( code Java 5.0 ) :
    String format = "La distance est de %,f metres";
    String libelle = String.format(Locale.FRENCH, format, 12345.67);
    System.out.println(libelle);
    libelle = String.format(Locale.ENGLISH, format, 12345.67);
    System.out.println(libelle);
Résultat :
La distance est de 12 345,670000 metres
La distance est de 12,345.670000 metres

S'il y a plus de données que de déterminants dans la chaîne de formatage alors les données supplémentaires sont ignorées.

Le comportement si une valeur est null dépend du format du déterminant concerné.

Les surcharges de la méthode format() lève une exception de type IllegalFormatException si la syntaxe de la chaîne indiquant le format n'est pas valide, si un déterminant n'est pas compatible avec la donnée correspondante ou s'il n'y a pas assez de données par rapport aux déterminants dans le format.

Elles lèvent une exception de type NullPointerException si la chaîne de formatage est null.

 

6.4.3.1. Le format de la chaîne de formatage

Le format est précisé sous la forme d'une chaîne de caractères qui peut contenir du texte brut et des spécificateurs de format.

Le format des spécificateurs de format pour des données générales, textuelles et numériques est de la forme :

%[argument_index$][flags][width][.precision]conversion

La partie optionnelle argument_index permet de préciser sous la forme d'une valeur entière l'index de l'argument dans le varargs fournis. Le premier argument est référencé par "1$".

La partie optionnelle flags est un ensemble de caractères pour préciser le format. Les flags utilisables sont dépendant de la conversion à réaliser.

La partie optionnelle width permet de préciser sous la forme d'une valeur entière le nombre minimum de caractères de la valeur.

La partie optionnelle precision permet de restreindre sous la forme d'une valeur entière le nombre de caractères de la valeur selon la conversion à réaliser.

La partie obligatoire conversion est un caractère qui permet de préciser comment l'argument sera formaté. Le caractère utilisé précise le type de données à convertir.

Le format des spécificateurs de format pour des données temporelles est de la forme :

%[argument_index$][flags][width]conversion

Les parties optionnelles argument_index, flags et width sont similaires à ceux du format précédent.

La partie obligatoire conversion est une séquence de 2 caractères. Le premier caractères est 't' ou 'T'. Le second caractère précise le format à utiliser.

Le format des spécificateurs de format qui ne correspondent pas aux arguments ont la syntaxe suivante :

%[flags][width]conversion

Les parties optionnelles flags et width sont similaires à ceux du format précédent.

La partie obligatoire conversion est une séquence d'un caractère qui précise le contenu à insérer.

 

6.4.3.2. Les conversions

Les conversions sont regroupées en plusieurs catégories :

  • général : s'applique à n'importe quel type
  • caractère : s'applique à des données textuelles qui représente un ou plusieurs caractères Unicode (char, Character, byte, Byte, short, Short et int ou Integer si leur valeur passée en paramètre à la méthode Character.isValidCodePoint() renvoie true)
  • numérique : pour des données entières (byte, Byte, short, Short, int, Integer, long, Long et BigItneger) ou pour des données flottantes (float, Float, double, Double et BigDecimal)
  • date/heure : pour des données temporelles (long, Long, Calendar, Date et TemporalAccessor)
  • pourcent : le littéral '%' ('\u0025')

Sauf mention contraire, le résultat du formatage d'une valeur null est la chaîne de caractères « null ».

Les conversions sont précisées avec le tableau des caractères ci-dessous. Les caractères en majuscules permettent de demander la mise en majuscules en utilisant la Locale par défaut si aucune n'est précisée explicitement en paramètre de la méthode.

Si aucune locale explicite n'est précisée, que ce soit lors de la construction de l'instance ou en tant que paramètre lors de l'invocation de la méthode, alors la Locale par défaut est utilisée.

Conversion

Catégorie

Description

'b', 'B'

Général

Si l'argument est null, alors le résultat est false. Si l'argument est un boolean ou un Boolean alors c'est le résultat de la méthode String.valueOf(arg). Sinon le résultat est true

'h', 'H'

Général

Le résultat de l'invocation de la méthode Integer.toHexString(arg.hashCode())

's', 'S'

Général

Si l'argument implémente java.util.Formattable alors c'est le résultat de l'invocation de sa méthode formatTo() sinon le résultat est l'invocation de sa méthode toString()

'c', 'C'

Caractère

Un caractère Unicode

'd'

Entier

Un nombre entier décimal

'o'

Entier

Un nombre entier octal

'x', 'X'

Entier

Un nombre entier hexadécimal

'e', 'E'

Flottant

Un nombre décimal en notation scientifique

'f'

Flottant

Un nombre flottant décimal

'g', 'G'

Flottant

Un nombre flottant utilisant la notation scientifique ou le format décimal, en fonction de la précision et de la valeur après arrondie

'a', 'A'

Flottant

Un nombre hexadécimal à virgule flottante avec un exposant. Les arguments de type BigDecimal ne sont pas supportés

't', 'T'

Date/heure

Le préfixe pour la conversion de données temporelles

'%'

Pourcent

Le caractère '%' ('\u0025')

'n'

Séparateur de ligne

Le séparateur de ligne spécifique à la plate-forme


 

6.4.3.3. Les conversions de données temporelles

La conversion des données temporelles utilise le préfixe 't' and 'T' suivi d'un caractère qui précise la conversion à réaliser. Les conversions utilisables pour les heures sont précisées dans le tableau ci-dessous :

'H'

Heure du jour formatée sur 24 heures avec deux chiffres avec un zéro de tête si nécessaire : 00 - 23

'I'

Heure du jour formatée sur 12 heures avec deux chiffres avec un zéro de tête si nécessaire : 01 - 12

'k'

Heure du jour formatée sur 24 heures : 0 - 23

'l'

Heure du jour formatée sur 24 heures : 0 - 12

'M'

Minute du jour formatée sur deux chiffres avec un zéro de tête si nécessaire : 00 - 59

'S'

Seconde du jour formatée sur deux chiffres avec un zéro de tête si nécessaire : 00 - 60 (60 est une valeur spéciale requise pour supporter les secondes intercalaires)

'L'

Milliseconde du jour formatée sur trois chiffres avec des zéros de tête si nécessaire : 000 - 999

'N'

Nanoseconde du jour formatée sur neuf chiffres avec des zéros de tête si nécessaire : 000000000 - 999999999

'p'

Matin ou après-midi en minuscules spécifiques selon la Locale. Préfixer par 'T' pour être en majuscules

'z'

Offset du fuseau horaire par rapport à l'heure GMT dans le style défini par la RFC 822 qui tient compte de l'heure d'été/hiver. Pour des valeurs de types long, Long et Date, le fuseau horaire par défaut de la JVM

'Z'

Une représentation abrégée du fuseau horaire qui tient compte de l'heure d'été/hiver. Pour des valeurs de types long, Long et Date, le fuseau horaire par défaut de la JVM

's'

Secondes écoulées depuis l'Epoch (1 janvier 1970 00:00:00 UTC)

'Q'

Millisecondes écoulées depuis l'Epoch (1 janvier 1970 00:00:00 UTC)


Exemple ( code Java 5.0 ) :
    Calendar heureDebut = new GregorianCalendar(1995, 0, 10, 8, 7, 6);
    Calendar heureFin = new GregorianCalendar(1995, 0, 10, 17, 7, 6);
    System.out.println(String.format("%tH", heureDebut));
    System.out.println(String.format("%tI", heureFin));
    System.out.println(String.format("%tk", heureDebut));
    System.out.println(String.format("%tl", heureFin));
    System.out.println(String.format("%tM", heureDebut));
    System.out.println(String.format("%tS", heureDebut));
    System.out.println(String.format("%tL", heureDebut));
    System.out.println(String.format("%tN", heureDebut));
    System.out.println(String.format("%tp", heureDebut));
    System.out.println(String.format("%Tp", heureDebut));
    System.out.println(String.format("%tp", heureFin));
    System.out.println(String.format("%Tp", heureFin));
    System.out.println(String.format("%tz", heureDebut));
    System.out.println(String.format("%tZ", heureDebut));
    System.out.println(String.format("%ts", heureDebut));
    System.out.println(String.format("%tQ", heureDebut));
Résultat :
08
05
8
5
07
06
000
000000000
am
AM
pm
PM
+0100
CET
789721626
789721626000

Les conversions utilisables pour les dates sont précisées dans le tableau ci-dessous :

'B'

Le nom long du mois selon la Locale :

Exemple : "janvier", "février"

'b'

Le nom abrégé du mois selon la Locale

Exemple : "janv.", "févr."

'h'

Identique à 'b'

'A'

Le nom long du jour de la semaine selon la Locale

Exemple : "lundi", "mardi"

'a'

Le nom cours du mois selon la Locale

Exemple : "lun.", "mar."

'C'

Année sur quatre chiffres divisée par 100, formatée en deux chiffres avec un zéro en tête si nécessaire

Exemple : 00, 99

'Y'

Année, formatée en au moins quatre chiffres avec des zéros de tête si nécessaire, par exemple 0536 équivaut à 536 après Jésus Christ dans le calendrier grégorien

'y'

Deux derniers chiffres de l'année avec un zéro en tête si nécessaire

Exemple : 00,99

'j'

Jour de l'année, formaté sur 3 chiffres avec des zéros en tête si nécessaire : 001 - 366

'm'

Mois de l'année, formaté sur 2 chiffres avec un zéro en tête si nécessaire : 01 - 12

'd'

Jour du mois, formaté sur 2 chiffres avec un zéro en tête si nécessaire : 01 - 31

'e'

Jour du mois, formaté sur 2 chiffres : 1 - 31


Exemple ( code Java 5.0 ) :
    Calendar dateLimite = new GregorianCalendar(1995, 3, 7);
    System.out.println(String.format("%tB", dateLimite));
    System.out.println(String.format("%tb", dateLimite));
    System.out.println(String.format("%th", dateLimite));
    System.out.println(String.format("%tA", dateLimite));
    System.out.println(String.format("%ta", dateLimite));
    System.out.println(String.format("%tC", dateLimite));
    System.out.println(String.format("%ty", dateLimite));
    System.out.println(String.format("%tY", dateLimite));
    System.out.println(String.format("%tj", dateLimite));
    System.out.println(String.format("%tm", dateLimite));
    System.out.println(String.format("%td", dateLimite));
    System.out.println(String.format("%te", dateLimite));
Résultat :
avril
avr.
avr.
vendredi
ven.
19
95
1995
097
04
07
7

Les conversions utilisables pour les dates/heures sont précisées dans le tableau ci-dessous :

'R'

Heure formatée au format 24 heures "%tH:%tM"

'T'

Heure formatée au format 24 heures "%tH:%tM:%tS"

'r'

Heure formatée au format 12 heures "%tI:%tM:%tS %Tp"

'D'

Date formatée au format "%tm/%td/%ty"

'F'

Date formatée au format ISO-8601 "%tY-%tm-%td"

'c'

Date et heure formatée au format "%ta %tb %td %tT %tZ %tY"

Exemple "dim. avr. 17 12:30:45 CEST 1988"


Tout caractère non explicitement défini comme suffixe de conversion pour date/heure est illégal et est réservé aux futures extensions.

Exemple ( code Java 5.0 ) :
    Calendar dateHeureLimite = new GregorianCalendar(2015, 6, 11, 11, 20, 45);
    System.out.println(String.format("%tR", dateHeureLimite));
    System.out.println(String.format("%tT", dateHeureLimite));
    System.out.println(String.format("%tr", dateHeureLimite));
    System.out.println(String.format("%tD", dateHeureLimite));
    System.out.println(String.format("%tF", dateHeureLimite));
    System.out.println(String.format("%tc", dateHeureLimite));
Résultat :
11:20
11:20:45
11:20:45 AM
07/11/15
2015-07-11
sam. juil. 11 11:20:45 CEST 2015

 

6.4.3.4. Les flags

Plusieurs flags sont utilisables selon le type de la valeur fournie en argument :

Flag

Général

Caractère

Numérique

Flottant

Date/Heure

Description

'-'

Oui

Oui

Oui

Oui

Oui

Justification à gauche

'#'

Oui 1

-

Oui 3

Oui

-

Le résultat dépendant d'une conversion spécifique

'+'

-

-

Oui 4

Oui

-

Toujours inclure le signe

'  '

-

-

Oui 4

Oui

-

Le résultat comprendra un espace en tête pour les valeurs positives

'0'

-

-

Oui

Oui

-

Complété avec des zéros

','

-

-

Oui 2

Oui 5

-

Inclure des séparateurs de groupement spécifiques à la Locale

'('

-

-

Oui 4

Oui 5

-

Entourés un nombre négatif par des parenthèses


1 Selon l'implémentation de la méthode formatTo() de l'interface java.util.Formattable

2 Uniquement pour les conversions de type 'd'

3 Uniquement pour les conversions de type 'o', 'x' et 'X'

4 Les conversions de type 'd', 'o', 'x' et 'X' pour BigInteger et les conversions de type 'd' pour byte, Byte, short, Short, int, Integer, long et Long

5 Uniquement pour les conversions de type 'e', 'E', 'f', 'g', et 'G'

Exemple ( code Java 5.0 ) :
    int montant = -1234;
    System.out.println(String.format("% d euros", montant));
    System.out.println(String.format("%(d euros", montant));
    System.out.println(String.format("%+d euros", montant));
    System.out.println(String.format("%+d euros", 1234));
    System.out.println(String.format(Locale.ENGLISH, "%,d euros", montant));
    System.out.println(String.format("%-10d euros", montant));
    System.out.println(String.format("%010d euros", montant));
Résultat :
-1234 euros
(1234) euros
-1234 euros
+1234 euros
-1,234 euros

Certains flags doivent obligatoirement être utilisés avec une taille.

Exemple ( code Java 5.0 ) :
    int montant = -1234;
    System.out.println(String.format("%-10d euros", montant));
    System.out.println(String.format("%010d euros", montant));
Résultat :
-1234      euros
-000001234 euros

 

6.4.3.5. La taille (Width)

La partie width permet de préciser le nombre minimum de caractères à l'issue de la conversion.

Exemple ( code Java 5.0 ) :
    int montant = -1234;
    System.out.println(String.format("Prix : %10d euros", montant));
Résultat :
Prix :      -1234 euros

 

6.4.3.6. La précision (Precision)

Pour les conversions de nombres flottants ('a', 'A', 'e', 'E' et 'f'), la précision définit le nombre de chiffres de la partie décimale. Si la conversion est 'g' ou 'G' alors la précision définit le nombre total de chiffres après l'arrondi.

La partie width permet de préciser le nombre minimum de caractères à l'issue de la conversion.

Exemple ( code Java 5.0 ) :
    double prix = 1234.5678;
    System.out.println(String.format("%.2f euros", prix));
Résultat :
1234,57 euros

Pour les conversions de caractères, nombres entiers et dates/heures, la précision n'est pas utilisable : si c'est le cas alors une exception est levée.

Pour les autres types d'argument, la précision définit le nombre maximum de caractères à l'issu de la conversion.

 

6.4.3.7. Les index des arguments

L'index des arguments est un nombre entier précisant la position de l'argument dans la liste des arguments. Le premier argument est référencé par "1$", le second par "2$", ...

Il est aussi possible d'utiliser "<" ("\u003c") qui permet de faire référence à l'index du spécificateur de format précédent.

Exemple ( code Java 5.0 ) :
    Calendar dateVal = new GregorianCalendar(2015, 6, 11);
    String libelle1 = String.format("Date de validité : %1$te/%1$tm/%1$tY", dateVal);
    System.out.println(libelle1);
    String libelle2 = String.format("Date de validité : %1te/%<tm/%<tY", dateVal);
    System.out.println(libelle2);
Résultat :
Date de validité : 11/07/2015
Date de validité : 11/07/2015

 

6.4.4. La conversion en minuscules ou en majuscules

Les méthodes toUpperCase() et toLowerCase() de la classe String permettent respectivement d'obtenir une nouvelle chaîne tout en majuscules ou tout en minuscules.

La méthode toLowerCase() permet de retourner une chaîne dont tous les caractères ont été transformés en minuscules.

Exemple :
    String message = "PRODUITS À 10 €";
    System.out.println(message.toLowerCase());
Résultat :
produits à 10 €

Sans paramètre, la méthode toUpperCase() utilise la Locale par défaut du système. Une surcharge de la méthode permet de préciser la Locale à utiliser.

Exemple :
    String message = "PRODUITS À 10 €";
    System.out.println(message.toLowerCase(Locale.ENGLISH));

La méthode toUpperCase() permet de retourner une chaîne dont tous les caractères ont été transformés en majuscules.

Exemple :
    String message = "Produits à 10 €";
    System.out.println(message.toUpperCase());
Résultat :
PRODUITS À 10 €

Sans paramètre, la méthode toUpperCase() utilise la Locale par défaut du système. Une surcharge de la méthode permet de préciser la Locale à utiliser.

Exemple :
    String message = "Produits à 10 €";
    System.out.println(message.toUpperCase(Locale.ENGLISH));

 

6.4.5. L'obtention d'une sous-chaîne

Les surcharges de la méthode substring() permettent d'obtenir une sous-chaîne.

La méthode substring(int, int) permet d'extraire la sous-chaîne composée des caractères compris entre l'index de début et celui de fin fournis en paramètres.

La surcharge substring(int) permet d'extraire la sous-chaîne composée des caractères compris entre l'index de début fourni en paramètre et celui de la fin de la chaîne.

Exemple :
    String message = "Produits à 10 €";
    System.out.println(message.substring(11));
    System.out.println(message.substring(0, 9));
Résultat :
10 €
Produits 

 

6.4.6. L'obtention de la taille d'une chaîne

La méthode length() renvoie la taille de la chaîne de caractères.

Exemple :
    String message = "Produits à 10 €";
    System.out.println(message.length());
Résultat :
15

 

6.4.7. Le découpage d'une chaîne de caractères

La méthode split() de la classe String permet de couper une chaîne de caractères selon le séparateur fourni en paramètre. Elle renvoie un tableau de chaînes qui contient les sous-éléments.

Exemple :
    String[] noms = "nom1,nom2,nom3".split(",");
    System.out.println(noms[1]);
Résultat :
nom2

 

6.4.8. La vérification du contenu d'une chaîne

Depuis Java 1.5, la méthode contains() de la classe String renvoie un booléen qui indique si la chaîne contient la séquence fournie en paramètre.

Exemple :
    String chaine = "Le langage Java est orienté objet";
    boolean contientJava = chaine.contains("Java");
    System.out.println(contientJava);    
Résultat :
true

 

6.4.9. L'obtention de la position d'un caractère ou d'une chaîne

Les surcharges de la méthode indexOf() permettent d'obtenir la première position de l'élément dans la chaîne de caractères. Celles qui attendent un int en second paramètre permet de préciser l'index à partir duquel la recherche doit s'effectuer. Elle retourne -1 si le caractère n'est pas trouvé.

La méthode indexOf(int) renvoie l'index de la première occurrence du caractère dans la chaîne dont le code Unicode est passé en paramètre.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.indexOf(97);
    System.out.println(pos);    
Résultat :
4

La surcharge indexOf(int, int) renvoie l'index de la première occurrence du caractère dans la chaîne à partir de la position fournie dont le code Unicode est passé en paramètre.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.indexOf(97, 8);
    System.out.println(pos);
Résultat :
12

La surcharge indexOf(String) renvoie l'index de la première occurrence de la chaîne fournie en paramètre ou -1 si la chaîne n'est pas trouvée.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.indexOf("a");
    System.out.println(pos);    
Résultat :
4

La surcharge indexOf(String, int) renvoie l'index de la première occurrence de la chaîne à partir de l'index fournis en paramètres ou -1 si la chaîne n'est pas trouvée.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.indexOf("a", 8);
    System.out.println(pos);
Résultat :
12

Les surcharges de la méthode lastIndexOf() permettent d'obtenir la dernière position de l'élément dans la chaîne de caractères. Celles qui attendent un int en second paramètre permet de préciser l'index à partir duquel la recherche doit s'effectuer. Elle retourne -1 si le caractère n'est pas trouvé.

La méthode lastIndexOf(int) renvoie l'index de la dernière occurrence du caractère dans la chaîne dont le code Unicode est passé en paramètre.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.lastIndexOf(103); // caractère 'g'

    System.out.println(pos);
Résultat :
8

La surcharge lastIndexOf(int, int) renvoie l'index de la dernière occurrence du caractère dans la chaîne en reculant à partir de la position fournie dont le code Unicode est passé en paramètre.

Exemple :
    String chaine = "Le langage Java";
    int pos = chaine.lastIndexOf(101, 5); // caractère 'e'

    System.out.println(pos);
Résultat :
1

La surcharge lastIndexOf(String) renvoie l'index de la dernière occurrence de la chaîne fournie en paramètre ou -1 si la chaîne n'est pas trouvée.

Exemple :
    String chaine = "Le langage Java est un gage de qualité";
    int pos = chaine.lastIndexOf("gage");
    System.out.println(pos);    
Résultat :
23

La surcharge lastIndexOf(String, int) renvoie l'index de la dernière occurrence de la chaîne à partir de l'index fourni en paramètre en reculant ou -1 si la chaîne n'est pas trouvée.

Exemple :
    String chaine = "Le langage Java est un gage de qualité";
    int pos = chaine.lastIndexOf("gage", 20);
    System.out.println(pos);
Résultat :
6

 

6.4.10. L'obtention d'un caractère de la chaîne

La méthode charAt() retourne le caractère se trouvant à l'index fourni en paramètre. L'index commence à la valeur 0. Si l'index fourni en paramètre n'est pas compris entre 0 et la taille de la chaîne - 1, alors une exception de type IndexOutOfBounsException est levée.

Exemple :
    String chaine = "Java";
    char car = chaine.charAt(2);
    System.out.println(car);
Résultat :
v

Depuis Java 1.5, la méthode codePointAt() retourne le codepoint Unicode du caractère se trouvant à l'index fourni en paramètre. Si l'index fourni en paramètre n'est pas compris entre 0 et la taille de la chaîne - 1, alors une exception de type IndexOutOfBounsException est levée.

Exemple ( code Java 5.0 ) :
    String chaine = "10€ les 3";
    int codepoint = chaine.codePointAt(2);
    System.out.println(codepoint);
Résultat :
8364

Depuis Java 1.5, la méthode codePointBefore() retourne le codepoint Unicode du caractère se trouvant avant l'index fourni en paramètre. Si l'index fourni en paramètre n'est compris entre 1 et la taille de la chaîne, alors une exception de type IndexOutOfBounsException est levée.

Exemple ( code Java 5.0 ) :
    String chaine = "10€ les 3";
    int codepoint = chaine.codePointBefore(3);
    System.out.println(codepoint);
Résultat :
8364

 

6.4.11. La suppression des espaces de début et de fin

La méthode trim() renvoie une chaîne de caractères dans laquelle les caractères dont le codepoint est inférieur ou égal à U+0020 en début ou en fin de chaîne sont retirés.

Si la chaîne est vide ou si les premiers et derniers caractères ne sont pas des espaces (comme défini ci-dessus), alors c'est la référence à la chaîne qui est renvoyée.

Si la chaîne ne contient que des caractères considérés comme des espaces, alors c'est une chaîne vide qui est retournée.

Exemple :
    String chaine = "  test  ";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.trim() + "*");
    chaine = "\ttest\t";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.trim() + "*");
Résultat :
*  test  *
*test*
*       test    *
*test*

Depuis Java 11, la méthode strip() renvoie une chaîne de caractères dans laquelle les caractères considérés comme des espaces en début ou en fin de chaîne sont retirés. Les caractères considérés comme des espaces sont différents de ceux considérés de la méthode trim() : ce sont les caractères dont le code point Unicode passé à la méthode Character.isWhitespace(int) renvoie true.

Si la chaîne est vide ou si tous les code points de la chaîne sont des caractères considérés comme des espaces, alors une chaîne vide est renvoyée. Sinon, il renvoie une sous-chaîne de la chaîne commençant par le premier code point qui n'est pas considéré comme un espace jusqu'au dernier code point qui n'est pas considéré comme un espace.

Exemple :
    String chaine = "  test  ";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.strip() + "*");
    chaine = "\ttest\t";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.strip() + "*");
    chaine = "\u00A0\u00A0test\u00A0\u00A0";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.strip() + "*");
    System.out.println(Character.isWhitespace('\u00A0'));
Résultat :
*  test  *
*test*
*       test    *
*test*
*  test  *
*  test  *
false

Depuis Java 11, la méthode stripLeading() renvoie une chaîne de caractères dans laquelle les caractères considérés comme des espaces en début de chaîne sont retirés. Les caractères considérés comme des espaces sont les caractères dont le code point Unicode passé à la méthode Character.isWhitespace() renvoie true.

Si la chaîne est vide ou si tous les code points de la chaîne sont des caractères considérés comme des espaces, alors une chaîne vide est renvoyée. Sinon, il renvoie une sous-chaîne de la chaîne commençant par le premier code point qui n'est pas considéré comme un espace jusqu'au dernier caractère de la chaîne.

Exemple :
    String chaine = "  test  ";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.stripLeading() + "*");
    chaine = "\ttest\t";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.stripLeading() + "*");
Résultat :
*  test  *
*test  *
*       test    *
*test   *

Depuis Java 11, la méthode stripTrailing() renvoie une chaîne de caractères dans laquelle les caractères considérés comme des espaces en fin de chaîne sont retirés. Les caractères considérés comme des espaces sont les caractères dont le code point Unicode passé à la méthode Character.isWhitespace() renvoie true.

Si la chaîne est vide ou si tous les code points de la chaîne sont des caractères considérés comme des espaces, alors une chaîne vide est renvoyée. Sinon, il renvoie une sous-chaîne de la chaîne commençant par le caractère de la chaîne jusqu'au dernier caractère qui n'est pas considéré comme un espace.

Exemple :
    String chaine = "  test  ";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.stripTrailing() + "*");
    chaine = "\ttest\t";
    System.out.println("*" + chaine + "*");
    System.out.println("*" + chaine.stripTrailing() + "*");
Résultat :
*  test  *
*  test*
*       test    *
*       test*

 

6.4.12. Tester si une chaîne est vide

Le plus simple est de tester l'égalité avec une chaîne vide en invoquant la méthode equals() sur une chaîne littérale vide.

Exemple :
    String chaine = "";
    if ("".equals(chaine)) {
      System.out.println("La chaine est vide");
    } 
Résultat :
La chaine est vide

Le plus performant est de tester si la taille de la chaîne est égale à zéro.

Exemple :
    String chaine = "";
    if (chaine != null && chaine.length() == 0) {
      System.out.println("La chaine est vide");
    }
Résultat :
La chaine est vide

La méthode isEmpty() ajoutée dans Java SE 6 facilite le test d'une chaîne de caractères vide. Cette méthode utilise les données de l'instance de l'objet, il est donc nécessaire de vérifier que cette instance n'est pas null pour éviter la levée d'une exception de type NullPointerException.

Exemple ( code Java 6 ) :
package fr.jmdoudoux.dej.java6;

public class TestEmptyString {
    
  public static void main(String args[]) {
        
    String chaine = null;
    try {
      if (chaine.isEmpty()){
        System.out.println("la chaine est vide");
      }
    } catch (NullPointerException e) {
      System.out.println("la chaine est null");
    }
        
    chaine = "test";
    if (chaine.isEmpty()){
      System.out.println("la chaine est vide");
    } else  {
      System.out.println("la chaine n'est pas vide");
    }
        
    chaine = "";
    if (chaine.isEmpty()){
      System.out.println("la chaine est vide");
    } else  {
      System.out.println("la chaine n'est pas vide");
    }   
  }    
}
Résultat :
la chaine est null
la chaine n'est pas vide
la chaine est vide

 

6.4.13. Tester si une chaîne est vide ou ne contient que des espaces

Depuis Java 11, la méthode isBlank() renvoie un booléen qui est true si la chaîne est vide ou ne contient que des code points Unicode qui sont considérés comme des espaces. Ce sont les caractères dont le code point Unicode passé à la méthode Character.isWhitespace() renvoie true.

Exemple ( code Java 11 ) :
    String chaine = null;
    try {
      System.out.println(chaine.isBlank());
    } catch (NullPointerException e) {
      System.out.println("la chaine est null");
    }

    chaine = "test";
    System.out.println(chaine.isBlank());

    chaine = "";
    System.out.println(chaine.isBlank());

    chaine = "  ";
    System.out.println(chaine.isBlank());
Résultat :
la chaine est null
false
true
true

 

6.4.14. Le remplacement de caractères ou de sous-chaînes

Plusieurs méthodes permettent de remplacer des caractères ou des sous-chaînes dans la chaîne de caractères.

La méthode replace(char, char) remplace tous les caractères égaux à celui fourni dans le premier paramètre par celui fourni dans le second.

Exemple ( code Java 5.0 ) :
    String chaine = "titi";
    String resultat = chaine.replace('i', 'o');
    System.out.println(resultat); 
Résultat :
toto

Depuis Java 1.5, la surcharge replace(CharSequence, CharSequence) remplace toutes occurrences de la sous-chaîne fournie en paramètre par celle fournie dans le second.

Exemple ( code Java 5.0 ) :
    String chaine = "titi";
    String resultat = chaine.replace("ti", "pa");
    System.out.println(resultat);
Résultat :
papa

La méthode replaceAll(String regex, String remplacement) remplace dans la chaîne toutes les sous-chaînes qui respectent l'expression régulière fournie par la chaîne fournie dans le second argument. Elle lève une exception de type PatternSyntaxException si la syntaxe de l'expression régulière n'est pas valide.

Exemple ( code Java 1.4 ) :
    String chaine = "titi";
    String resultat = chaine.replaceAll("ti", "ta");
    System.out.println(resultat);
Résultat :
tata

La méthode replaceFirst(String regex, String remplacement) remplace dans la chaîne la première sous-chaîne qui respecte l'expression régulière fournie par la chaîne fournie dans le second argument. Elle lève une exception de type PatternSyntaxException si la syntaxe de l'expression régulière n'est pas valide.

Exemple ( code Java 1.4 ) :
    String chaine = "titi";
    String resultat = chaine.replaceFirst("ti", "ta");
    System.out.println(resultat);
    resultat = chaine.replaceFirst("ti$", "ta");
    System.out.println(resultat); 
Résultat :
tati
tita

 

6.5. La conversion de et vers une chaîne de caractères

Plusieurs méthodes de la classe String ou d'autres classes permettent de convertir de et vers une chaîne de caractères.

 

6.5.1. La conversion d'une chaîne en tableau d'octets

Depuis Java 1 .1, la méthode getBytes() de la classe String retourne un tableau d'octets encodés avec le jeu de caractères par défaut du système. Elle possède plusieurs surcharges.

Afin d'améliorer la portabilité, il est préférable d'utiliser une surcharge de la méthode getBytes() qui attend en paramètre le jeu de caractères à utiliser pour l'encodage. Soit depuis java 1.1 avec son nom sous la forme d'une chaîne de caractères soit depuis Java 1.6 avec une instance de la classe java.nio.charset.CharSet.

Exemple :
    String message = "Produits à 10 €";

    byte[] bytes = message.getBytes();
    System.out.println(Arrays.toString(bytes));

    bytes = message.getBytes(StandardCharsets.US_ASCII);
    System.out.println(Arrays.toString(bytes));

    bytes = message.getBytes("UTF-8");
    System.out.println(Arrays.toString(bytes));
Résultat :
[80, 114, 111, 100, 117, 105, 116, 115, 32, -32, 32, 49, 48, 32, -128]
[80, 114, 111, 100, 117, 105, 116, 115, 32, 63, 32, 49, 48, 32, 63]
[80, 114, 111, 100, 117, 105, 116, 115, 32, -61, -96, 32, 49, 48, 32, -30, -126, -84]

La surcharge getBytes(int, int, byte[], int) est dépréciée depuis Java 1.1 car elle ne convertit pas correctement les caractères en octets.

 

6.5.2. La conversion d'une chaîne en tableau de caractères

La méthode toCharArray() renvoie une tableau de caractères qui est une copie du tableau de caractères interne de l'instance de la chaîne.

Exemple :
    String chaine1 = "test";
    char[] caracteres = chaine1.toCharArray();
    System.out.println(caracteres[1]);
Résultat :
e

 

6.5.3. La conversion d'un type primitif en chaîne

Les différentes surcharges de la méthode statique valueOf() permettent de convertir la valeur fournie en paramètre en chaîne de caractères. Celles-ci attendent en paramètre une valeur primitive ou un objet.

Exemple :
    String chaine = String.valueOf(123);
    System.out.println(chaine);
    chaine = String.valueOf(123.45);
    System.out.println(chaine);
Résultat :
123
123.45

 

6.5.4. La conversion d'un objet en chaîne de caractères

Pour convertir un objet en une chaîne de caractères, et donc en obtenir une représentation textuelle de l'objet, il faut utiliser la méthode toString() sur l'instance concernée.

Chaque classe héritent de la méthode toString() définie dans la classe Object directement ou indirectement d'une des redéfinitions de ses classes mères. Une classe peut aussi redéfinir la méthode toString() selon ses besoins pour fournir une représentation textuelle de son état.

Exemple :
    Date maintenant = new Date();
    String libelle = aujourdhui.toString();
    System.out.println(libelle);
Résultat :
Sun Sep 16 15:41:01 CEST 2001

 

6.5.5. La conversion d'une chaîne en entier

La méthode parseInt() de la classe Integer permet de convertir une chaîne de caractères contenant une valeur numérique dans sa valeur entière.

Exemple :
    int taille = Integer.parseInt("175");
    system.out.println(taille);
Résultat :
175

La chaîne de caractères doit contenir des chiffres mais peut aussi commencer par un signe + (\u002B) ou - (\u002D)

Par défaut, la méthode parseInt() utilise la base 10. Une surcharge de la méthode attend en paramètre la base à utiliser.

Exemple :
    int taille = Integer.parseInt("20", 8);
    system.out.println(taille);
Résultat :
16

 

6.6. La concaténation de chaînes de caractères

Elle peut se faire de plusieurs manières, selon la version de Java utilisée et les besoins :

  • l'opérateur +
  • la méthode concat() de la classe String
  • la classe StringBuffer
  • la classe StringBuilder
  • la classe StringJoiner
  • la méthode join() de la classe String

 

6.6.1. La concaténation avec l'opérateur +

Le plus simple est d'utiliser un raccourci syntaxique proposé dans le langage qui repose sur l'utilisation de l'opérateur +. Java admet l'opérateur + comme opérateur pour la concaténation de chaînes de caractères.

Exemple :
    String chaine1 = "Hello";
    String chaine2 = "World";
    String chaine3 = chaine1 + " " + chaine2;
    System.out.println(chaine3);
Résultat :
Hello World

Il est possible de concaténer des chaînes avec des types primitifs en utilisant l'opérateur +.

Exemple :
    String libelle = "Produits à " + 10 + " euros";
    System.out.println(libelle);
Résultat :
Produits à 10 euros

L'opérateur + permet de concaténer plusieurs chaînes de caractères. Il est possible d'utiliser l'opérateur += pour concaténer une chaîne avec une autre et affecter la nouvelle chaîne à la variable.

Exemple :
    String texte = "";
    texte += "Hello";
    texte += " ";
    texte += "world";
    System.out.println(texte);
Résultat :
Hello world

Cet opérateur sert aussi à concaténer des chaînes avec tous les types de bases. La variable ou constante est alors convertie en chaîne et ajoutée à la précédente. La condition préalable est d'avoir au moins une chaîne dans l'expression sinon le signe '+' est évalué comme opérateur mathématique.

Exemple :
    System.out.println("La valeur de PI est : " + Math.PI);
    int duree = 121;
    System.out.println("Durée = " + duree);
Résultat :
La valeur de PI est : 3.141592653589793
Durée = 121

Attention à l'ordre d'évaluation des opérateurs + lorsque dans la même expression ils sont utilisés pour ajouter des valeurs numériques et pour concaténer des chaînes. Cet ordre peut être modifié en utilisant des parenthèses.

Exemple :
    String libelle = "Produits à " + 5 + 5 + " euros";
    System.out.println(libelle);

    libelle = "Produits à " + (5 + 5) + " euros";
    System.out.println(libelle);
Résultat :
Produits à 55 euros
Produits à 10 euros

Remarque : le compilateur va, selon la version de Java utilisée, mettre en oeuvre différentes techniques pour réaliser la concaténation :

  • à partir de Java 7, la concaténation en dehors des boucles est faite avec un StringBuilder
  • à partir de Java 9, la concaténation est effectuée par une fonctionnalité fournie par la JVM invoquée via InvokeDynamic

 

6.6.2. La concaténation avec la méthode concat()

La méthode concat() de la classe String renvoie une chaîne de caractères qui est la concaténation d'elle-même avec celle fournie en paramètre.

Exemple :
    String chaine1 = "Hello";
    String chaine2 = "World";
    String chaine3 = chaine1.concat(" ").concat(chaine2);
    System.out.println(chaine3);
Résultat :
Hello World

 

6.6.3. La classe StringBuffer

Les objets de cette classe permettent de manipuler un tampon de caractères pour construire dynamiquement une chaîne de caractères. La taille du tampon évolue donc dynamiquement en fonction des méthodes invoquées.

Un objet de type StringBuffer peut être utilisé pour construire ou modifier une chaîne de caractères chaque fois que l'utilisation de la classe String nécessiterait de nombreuses instanciations d'objets temporaires.

La classe StringBuffer propose plusieurs constructeurs :

Constructeur

Rôle

StringBuffer()

Retourner une instance dont le tampon est initialisé avec une taille de 16 caractères

StringBuffer​(int capacity)

Retourner une instance dont la taille du tampon est fournie est paramètre

StringBuffer​(CharSequence seq)

Retourner une instance dont le tampon est initialisé avec la CharSequence fournie en paramètre. Depuis Java 1.5

StringBuffer​(String str)

Retourner une instance dont le tampon est initialisé avec la chaîne fournie en paramètre


La classe StringBuffer dispose de nombreuses méthodes qui permettent de modifier dynamiquement le tampon contenant les caractères :

Méthode

Rôle

StringBuffer append​(boolean b)

Ajouter une représentation de la valeur booléenne fournie en paramètre

StringBuffer append​(char c)

Ajouter le caractère fourni en paramètre

StringBuffer append​(char[] str)

Ajouter le tableau de caractère fourni en paramètre

StringBuffer append​(char[] str, int offset, int len)

Ajouter le sous-tableau fourni et précisé en paramètre

StringBuffer append​(double d)

Ajouter une représentation de la valeur de type double fournie en paramètre

StringBuffer append​(float f)

Ajouter une représentation de la valeur de type float fournie en paramètre

StringBuffer append​(int i)

Ajouter une représentation de la valeur de type int fournie en paramètre

StringBuffer append​(long lng)

Ajouter une représentation de la valeur de type long fournie en paramètre

StringBuffer append​(CharSequence s)

Ajouter la CharSequence fournie en paramètre. Depuis Java 1.5

StringBuffer append​(CharSequence s, int start, int end)

Ajouter la sous-séquence fournie et précisée en paramètre. Depuis Java 1.5

StringBuffer append​(Object obj)

Ajouter la représentation textuelle de l'objet fourni en paramètre

StringBuffer append​(String str)

Ajouter la chaîne fournie en paramètre

StringBuffer append​(StringBuffer sb)

Ajouter le StringBuffer fourni en paramètre. Depuis Java 1.4

StringBuffer appendCodePoint​(int codePoint)

Ajouter une représentation textuelle du codePoint Unicode fourni en paramètre. Depuis Java 1.5

int capacity()

Renvoyer la capacité courante du tampon

char charAt​(int index)

Renvoyer le caractère dans le tampon à l'index fourni en paramètre

IntStream chars()

Renvoyer un IntStream dont les éléments sont les caractères sous la forme d'entier. Depuis Java 9

int codePointAt​(int index)

Renvoyer le code point Unicode du caractère à l'index fourni dans le tampon. Depuis Java 1.5

int codePointBefore​(int index)

Renvoyer le code point Unicode du caractère avant l'index fourni dans le tampon. Depuis Java 1.5

int codePointCount​(int beginIndex, int endIndex)

Renvoyer le nombre de code points Unicode dans la place d'index. Depuis Java 1.5

IntStream codePoints()

Renvoyer un IntStream dont les éléments sont les code points Unicode . Depuis Java 9

int compareTo​(StringBuffer another)

Comparer de manière lexicographique le tampon avec celui du StringBuffer fourni en paramètre. Depuis Java 11

StringBuffer delete​(int start, int end)

Retirer la sous-chaîne dont la plage d'index est fournie en paramètre. Depuis Java 1.2

StringBuffer deleteCharAt​(int index)

Retirer le caractère à l'index fourni en paramètre. Depuis Java 1.2

void ensureCapacity​(int minimumCapacity)

Définir la capacité minimale du tampon

void getChars​(int srcBegin, int srcEnd, char[] dst, int dstBegin)

Copier les caractères de la plage précisés dans le tableau fourni en paramètre

int indexOf​(String str)

Renvoyer l'index de la première occurrence de la chaîne fournie en paramètre

int indexOf​(String str, int fromIndex)

Renvoyer l'index de la première occurrence de la sous-chaîne spécifiée, à partir de l'index spécifié

StringBuffer insert​(int offset, boolean b)

Insérer une représentation de la valeur booléenne à la position précisée

StringBuffer insert​(int offset, char c)

Insérer le caractère à la position précisée

StringBuffer insert​(int offset, char[] str)

Insérer les caractères du tableau à la position précisée

StringBuffer insert​(int index, char[] str, int offset, int len)

Insérer les caractères de la plage du tableau à la position précisée. Depuis Java 1.2

StringBuffer insert​(int offset, double d)

Insérer une représentation de la valeur de type double à la position précisée

StringBuffer insert​(int offset, float f)

Insérer une représentation de la valeur de type float à la position précisée

StringBuffer insert​(int offset, int i)

Insérer une représentation de la valeur de type int à la position précisée

StringBuffer insert​(int offset, long l)

Insérer une représentation de la valeur de type long à la position précisée

StringBuffer insert​(int dstOffset, CharSequence s)

Insérer la CharSequence à la position précisée. Depuis Java 1.5

StringBuffer insert​(int dstOffset, CharSequence s, int start, int end)

Insérer les caractères de la plage de CharSequence à la position précisée. Depuis Java 1.5

StringBuffer insert​(int offset, Object obj)

Insérer la représentation textuelle de l'objet fourni à la position précisée

StringBuffer insert​(int offset, String str)

Insérer la chaîne fournie à la position fournie en paramètres

int lastIndexOf​(String str)

Renvoyer l'index de la dernière position de la chaîne fournie en paramètre. Depuis Java 1.4

int lastIndexOf​(String str, int fromIndex)

Renvoyer l'index de la dernière occurrence de la sous-chaîne spécifiée, en recherchant à rebours à partir de l'index spécifié. Depuis Java 1.4

StringBuffer replace​(int start, int end, String str)

Remplacer la séquence de caractères indiquée par la plage d'index par la chaîne fournie en paramètre. Depuis Java 1.2

StringBuffer reverse()

Inverser l'ordre des caractères du tampon. Depuis Java 1.0.2

void setCharAt​(int index, char ch)

Remplacer le caractère à l'index par celui fourni en paramètres

void setLength​(int newLength)

Préciser la taille de la chaîne de caractères. Si elle est plus petite que la chaîne actuelle, alors elle est tronquée. Si elle est plus grande, alors elle est complétée avec des caractères \u0000

CharSequence subSequence​(int start, int end)

Renvoyer une CharSequence qui encapsule la sous-chaîne des éléments dont la plage d'index est fournie en paramètre. Depuis Java 1.4

String substring​(int start)

Renvoyer une sous-chaîne qui contient les caractères à partir de l'index fourni. Depuis Java 1.2

String substring​(int start, int end)

Renvoyer une sous-chaîne qui contient les caractères contenus dans la plage d'index fournis. Depuis Java 1.2

void trimToSize()

Tenter de réduire la taille du tampon pour l'aligner sur la taille de la séquence de caractères. Depuis Java 1.5


Exemple :
public class TestStringBuffer {

  static final String lettreMin = "abcdefghijklmnopqrstuvwxyz";
  static final String lettreMaj = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  public static void main(java.lang.String[] args) {
    System.out.println(mettreEnMaj("chaine avec MAJ et des min"));
  }

  public static String mettreEnMaj(String s) {
    StringBuffer sb = new StringBuffer(s);

    for (int i = 0; i < sb.length(); i++) {
      int index = lettreMin.indexOf(sb.charAt(i));
      if (index >= 0)
        sb.setCharAt(i, lettreMaj.charAt(index));
    }
    return sb.toString();
  }
}
Résultat :
CHAINE AVEC MAJ ET DES MIN

 

6.6.4. La classe StringBuilder

Depuis Java 1.5, la classe java.lang.StringBuilder permet de manipuler un tampon de caractères pour construire dynamiquement une chaîne de caractères. La taille du tampon évolue donc dynamiquement en fonction des méthodes invoquées.

Un objet de type StringBuilder peut être utilisé pour construire ou modifier une chaîne de caractères chaque fois que l'utilisation de la classe String nécessiterait de nombreuses instanciations d'objets temporaires.

La classe StringBuilder propose plusieurs constructeurs :

Constructeur

Rôle

StringBuilder()

Retourner une instance dont le tampon est initialisé avec une taille de 16 caractères

StringBuilder​(int capacity)

Retourner une instance dont la taille du tampon est fournie est paramètre

StringBuilder​(CharSequence seq)

Retourner une instance dont le tampon est initialisé avec la CharSequence fournie en paramètre

StringBuilder(String str)

Retourner une instance dont le tampon est initialisé avec la chaîne fournie en paramètre


La classe StringBuilder dispose de nombreuses méthodes qui permettent de modifier dynamiquement le tampon contenant les caractères :

Méthode

Rôle

StringBuffer append​(boolean b)

Ajouter une représentation de la valeur booléenne fournie en paramètre

StringBuffer append​(char c)

Ajouter le caractère fourni en paramètre

StringBuffer append​(char[] str)

Ajouter le tableau de caractère fourni en paramètre

StringBuffer append​(char[] str, int offset, int len)

Ajouter le sous-tableau fourni et précisé en paramètre

StringBuffer append​(double d)

Ajouter une représentation de la valeur de type double fournie en paramètre

StringBuffer append​(float f)

Ajouter une représentation de la valeur de type float fournie en paramètre

StringBuffer append​(int i)

Ajouter une représentation de la valeur de type int fournie en paramètre

StringBuffer append​(long lng)

Ajouter une représentation de la valeur de type long fournie en paramètre

StringBuffer append​(CharSequence s)

Ajouter la CharSequence fournie en paramètre

StringBuffer append​(CharSequence s, int start, int end)

Ajouter la sous-séquence fournie et précisée en paramètre

StringBuffer append​(Object obj)

Ajouter la représentation textuelle de l'objet fourni en paramètre

StringBuffer append​(String str)

Ajouter la chaîne fournie en paramètre

StringBuffer append​(StringBuffer sb)

Ajouter le StringBuffer fourni en paramètre

StringBuffer appendCodePoint​(int codePoint)

Ajouter une représentation textuelle du codePoint Unicode fourni en paramètre

int capacity()

Renvoyer la capacité courante du tampon

char charAt​(int index)

Renvoyer le caractère dans le tampon à l'index fourni en paramètre

IntStream chars()

Renvoyer un IntStream dont les éléments sont les caractères sous la forme d'entier. Depuis Java 9

int codePointAt​(int index)

Renvoyer le code point Unicode du caractère à l'index fourni dans le tampon

int codePointBefore​(int index)

Renvoyer le code point Unicode du caractère avant l'index fourni dans le tampon

int codePointCount​(int beginIndex, int endIndex)

Renvoyer le nombre de code points Unicode dans la place d'index

IntStream codePoints()

Renvoyer un IntStream dont les éléments sont les code points Unicode . Depuis Java 9

int compareTo​(StringBuffer another)

Comparer de manière lexicographique le tampon avec celui du StringBuffer fourni en paramètre. Depuis Java 11

StringBuffer delete​(int start, int end)

Retirer la sous-chaînes dont la plage d'index est fournie en paramètre

StringBuffer deleteCharAt​(int index)

Retirer le caractère à l'index fourni en paramètre

void ensureCapacity​(int minimumCapacity)

Définir la capacité minimale du tampon

void getChars​(int srcBegin, int srcEnd, char[] dst, int dstBegin)

Copier les caractères de la plage précisés dans le tableau fourni en paramètre

int indexOf​(String str)

Renvoyer l'index de la première occurrence de la chaîne fournie en paramètre

int indexOf​(String str, int fromIndex)

Renvoyer l'index de la première occurrence de la sous-chaîne spécifiée, à partir de l'index spécifié

StringBuffer insert​(int offset, boolean b)

Insérer une représentation de la valeur booléenne à la position précisée

StringBuffer insert​(int offset, char c)

Insérer le caractère à la position précisée

StringBuffer insert​(int offset, char[] str)

Insérer les caractères du tableau à la position précisée

StringBuffer insert​(int index, char[] str, int offset, int len)

Insérer les caractères de la plage du tableau à la position précisée

StringBuffer insert​(int offset, double d)

Insérer une représentation de la valeur de type double à la position précisée

StringBuffer insert​(int offset, float f)

Insérer une représentation de la valeur de type float à la position précisée

StringBuffer insert​(int offset, int i)

Insérer une représentation de la valeur de type int à la position précisée

StringBuffer insert​(int offset, long l)

Insérer une représentation de la valeur de type long à la position précisée

StringBuffer insert​(int dstOffset, CharSequence s)

Insérer la CharSequence à la position précisée

StringBuffer insert​(int dstOffset, CharSequence s, int start, int end)

Insérer les caractères de la plage de CharSequence à la position précisée

StringBuffer insert​(int offset, Object obj)

Insérer la représentation textuelle de l'objet fourni à la position précisée

StringBuffer insert​(int offset, String str)

Insérer la chaîne fournie à la position fournie en paramètres

int lastIndexOf​(String str)

Renvoyer l'index de la dernière position de la chaîne fournie en paramètre

int lastIndexOf​(String str, int fromIndex)

Renvoyer l'index de la dernière occurrence de la sous-chaîne spécifiée, en recherchant à rebours à partir de l'index spécifié

StringBuffer replace​(int start, int end, String str)

Remplacer la séquence de caractères indiquée par la plage d'index par la chaîne fournie en paramètre

StringBuffer reverse()

Inverser l'ordre des caractères du tampon

void setCharAt​(int index, char ch)

Remplacer le caractère à l'index par celui fourni en paramètres

void setLength​(int newLength)

Préciser la taille de la chaîne de caractères. Si elle est plus petite que la chaîne actuelle, alors elle est tronquée. Si elle est plus grande, alors elle est complétée avec des caractères \u0000

CharSequence subSequence​(int start, int end)

Renvoyer une CharSequence qui encapsule la sous-chaîne des éléments dont la plage d'index est fournie en paramètre

String substring​(int start)

Renvoyer une sous-chaîne qui contient les caractères à partir de l'index fourni

String substring​(int start, int end)

Renvoyer une sous-chaîne qui contient les caractères contenus dans la plage d'index fournis

void trimToSize()

Tenter de réduire la taille du tampon pour l'aligner sur la taille de la séquence de caractères

 

6.6.5. Différence entre StringBuilder et StringBuffer

Les classes StringBuffer et StringBuilder ont des rôles et des méthodes similaires. Leur différence est dans leur contexte d'utilisation :

  • StringBuffer : les méthodes sont synchronisées, ce qui permet une utilisation dans un contexte multi-thread
  • StringBuilder : à n'utiliser que dans un contexte mono-thread (exemple : comme variable locale dans une méthode). Son utilisation dans un contexte multi-thread induit un risque de résultat erroné

Ainsi l'utilisation d'une instance de StringBuffer dans un contexte mono-thread n'a aucun impact sur les résultats mais uniquement sur les performances qui seront dans ce cas légèrement dégradée.

Par contre, l'utilisation d'une instance de StringBuilder dans un contexte multi-thread pourra induire des résultats erronés. Dans ce contexte, il est important d'utiliser un StringBuffer pour s'éviter des ennuis.

 

6.6.6. La classe StringJoiner

La classe java.util.StringJoiner, ajoutée en Java 8, permet de concaténer des chaînes de caractères avec un séparateur et éventuellement un préfixe et un suffixe.

La classe StringJoiner implémente le design pattern builder. Les différentes chaînes à ajouter sont précisées en les passant chacune en paramètre de la méthode add(). La méthode add() renvoie l'instance elle-même, ce qui permet de chaîner l'appel de plusieurs méthodes.

La méthode toString() permet d'obtenir le résultat de la concaténation.

Elle possède deux constructeurs.

Le premier attend en paramètre le séparateur à utiliser entre chaque chaîne.

Exemple ( code Java 8 ) :
    StringJoiner joiner = new StringJoiner(",");
    joiner.add("1")
          .add("2")
          .add("3");
    System.out.println(joiner.toString());
Résultat :
1,2,3

Le second constructeur attend trois paramètres :

  • le séparateur à utiliser entre les chaînes
  • le préfixe à utiliser dans le résultat
  • le suffixe à utiliser dans le résultat
Exemple ( code Java 8 ) :
    StringJoiner joiner = new StringJoiner(",", "[", "]");
    joiner.add("1")
          .add("2")
          .add("3");
    System.out.println(joiner.toString());
Résultat :
[1,2,3]

Si aucune chaîne n'est ajoutée, alors la chaîne obtenue est simplement la concaténation du préfixe et du suffixe.

Exemple ( code Java 8 ) :
    StringJoiner joiner = new StringJoiner(",", "[", "]");
    System.out.println(joiner.toString());
Résultat :
[]

 

6.6.7. La méthode join() de la classe String

La méthode statique join() de la classe String est ajoutée en Java 8. Elle permet de concaténer des chaînes entre-elles avec un séparateur. Elle possède deux surcharges.

La première attend en paramètre un délimiteur et un varargs de CharSequence.

Exemple ( code Java 8 ) :
    String[] valeurs = { "1", "2", "3" };
    String chaine = String.join(", ", valeurs);
    System.out.println(chaine);
Résultat :
1, 2, 3

La seconde surcharge attend en paramètre un délimiteur et un Iterable< ? extends CharSequence>.

Exemple ( code Java 8 ) :
    List<String> valeurs = List.of("1", "2", "3");
    String chaine = String.join(", ", valeurs);
    System.out.println(chaine);
Résultat :
1, 2, 3

 

6.7. La classe StringTokenizer

Cette classe permet de découper différentes portions d'une chaîne de caractères en fonction d'un ou plusieurs séparateurs.

La classe StringTokenizer possède plusieurs constructeurs permettant notamment de préciser la chaîne à décomposer et une chaîne contenant le séparateur à utiliser :

Constructeur

Rôle

StringTokenizer​(String str)

Renvoyer une instance qui utilise les délimiteurs par défaut " \t\n\r\f" (espace, tabulation, nouvelle ligne, retour chariot et form feed). Les délimiteurs ne sont pas retournés dans les éléments extraits

StringTokenizer​(String str, String delim)

Renvoyer une instance qui utilise le délimiteur passé en second paramètre. Les délimiteurs ne sont pas retournés dans les éléments extraits

StringTokenizer​(String str, String delim, boolean returnDelims)

Renvoyer une instance qui utilise le délimiteur passé en second paramètre. Le troisième paramètre est un booléen qui précise si les délimiteurs sont retournés dans les éléments extraits ou pas


Elle possède plusieurs méthodes pour fournir un contrôle d'itération afin de parcourir les différents éléments :

Méthode

Rôle

int countTokens()

Retourner le nombre d'éléments restant, soit le nombre de fois que la méthode nextToken() peut être invoquée avant qu'elle ne lève une exception

boolean hasMoreElements()

boolean hasMoreTokens()

Retourner true s'il reste encore au moins élément à obtenir sinon false

Object nextElement()

Retourner l'élément suivant sous la forme d'un Object s'il existe sinon lève une exception de type NoSuchElementException

String nextToken()

Retourner l'élément suivant sous la forme d'un String s'il existe sinon lève une exception de type NoSuchElementException

String nextToken​(String delim)

Retourner l'élément suivant, en utilisant les délimiteurs fournis, s'il existe sinon lève une exception de type NoSuchElementException


Exemple ( code Java 1.1 ) :

Exemple :
import java.util.*;

class TestStringTokenizer {
  public static void main(String args[]) {
    StringTokenizer st = new StringTokenizer("chaine1,chaine2,chaine3,chaine4",",");
    while (st.hasMoreTokens()) {
      System.out.println((st.nextToken()).toString());
    }
  }
}
Résultat :
chaine1
chaine2
chaine3
chaine4

 

6.8. Les chaînes de caractères et la sécurité

La classe java.lang.String est final, ce qui garantit que celle-ci ne peut pas avoir de classe fille et donc de s'assurer de son comportement. Comme les chaînes de caractères sont immuables, elles ne peuvent pas être modifiées. Ce comportement ne permet pas d'écraser ou de mettre à zéro son contenu, ce qui rend les chaînes inappropriées pour le stockage d'informations sensibles.

Avant Java 7, les chaînes de caractères sont stockées dans la permanent generation et ne sont donc jamais récupérées par le ramasse-miettes.

A partir de Java 7, les chaînes de caractères peuvent rester dans le heap jusqu'à ce que le ramasse-miettes récupère les instances inutilisées. Avant l'exécution de cette action, il est possible d'obtenir les données en obtenant et en exploitant un heap dump.

Il est dès lors préférable de stocker des données textuelles sensibles sous la forme d'un tableau de caractères plutôt que sous la forme de chaîne de caractères. Il est notamment possible de réinitialiser le tableau de caractères une fois que les données ne sont plus utiles pour les traitements.

Attention cependant, l'utilisation d'un char[] plutôt qu'une String ne garantit pas la sécurisation des données : cela offre la possibilité de réduire les possibilités pour obtenir ces données.

 

6.9. Le stockage en mémoire

Selon la manière dont une chaîne est créée, elle n'est pas stockée de la même manière en mémoire. Une instance de type String peut être stockée dans deux espaces :

  • le pool de String : lorsqu'une chaîne est créée et si la chaîne existe déjà dans le pool, la référence de la chaîne existante sera retournée, au lieu de créer un nouvel objet et de retourner sa référence
  • heap : chaque objet String dans le heap a son propre espace comme tout autre objet, même si deux objets String ont le même contenu

Comme les chaînes de caractères sont immuables, la JVM peut ne stocker qu'une seule copie d'une chaîne de caractères littérales. Ce comportement se nomme interning et repose sur l'utilisation du pool de String.

Lors de l'utilisation de la syntaxe littérale, la chaîne obtenue est stockée dans le pool de String. Dans les versions antérieures à la version 7 de Java, le pool de String est stocké dans la Permanent Generation. A partir de Java 7, le pool de String est stocké dans le heap.

Lors de la création d'une nouvelle instance en utilisant l'instruction new, une nouvelle instance est créée dans le heap même si une instance qui encapsule la même chaîne est présente dans le pool de String.

 

6.9.1. Le pool de String

La JVM dispose d'un pool de String, nommé String constant pool : c'est une région de la mémoire de la JVM dans laquelle elle peut stocker des instances de chaînes de caractères uniques.

Cela permet d'éviter d'avoir plusieurs instances avec la même valeur et ainsi d'améliorer les performances grâce à :

  • la réduction de la quantité de mémoire utilisée
  • la réduction du nombre d'allocations
  • la réduction de l'activité du ramasse-miettes

Le pool de Strings agit comme un cache pour les chaînes de caractères :

  • la JVM stocke une seule instance d'une même chaîne dans le pool
  • lors de la création d'une nouvelle chaîne, la JVM cherche si une chaîne avec la même valeur est présente dans le pool
  • si elle est trouvée, alors c'est la référence existante qui est utilisée
  • si elle n'est pas trouvée, alors elle est ajoutée dans le pool et c'est sa référence qui est utilisée. Ce mécanisme est nommé interning

La mise en cache des chaînes de caractères et leur réutilisation permet de réduire la consommation mémoire et améliore les performances. Ceci est possible car la classe String est immuable.

Les chaînes de caractères littérales sont automatiquement « interned » donc mises dans le pool si elles n'y sont pas déjà.

Exemple :
    String chaine1 = "test";
    String chaine2 = "test";
    System.out.println(chaine1 == chaine2);
Résultat :
true

Attention : ce mécanisme n'est pas mis en oeuvre lors de la création d'une instance de type String en utilisant un de ces constructeurs. Dans ce cas, une nouvelle instance est créée, même si elle existe déjà dans le pool de Strings.

Lors de la création d'une chaîne de caractères en utilisant un des constructeurs de la classe String, une nouvelle instance sera créée dans le heap, en dehors du pool de String.

Exemple :
    String chaine1 = "test";
    String chaine2 = "test";
    String chaine3 = new String("test");
    System.out.println(chaine1 == chaine2);
    System.out.println(chaine1 == chaine3);
Résultat :
true
false

Il est possible de forcer une instance à être incluse dans le pool de chaînes en utilisant la méthode intern() de l'instance. La méthode intern() ajoute la chaîne de caractères au pool de chaînes si elle n'y existe pas déjà, et renvoie la référence de cette chaîne « internée » :

Exemple :
    String chaine1 = "test";
    String chaine2 = "test";
    String chaine3 = new String("test");
    System.out.println(chaine1 == chaine2);
    System.out.println(chaine1 == chaine3);
    chaine3 = chaine3.intern();
    System.out.println(chaine1 == chaine3);
Résultat :
true
false
true

Les chaînes de caractères contenues dans le pool de Strings sont récupérables par le ramasse-miettes s'il n'existe plus aucune référence vers leurs instances.

 

6.10. Les blocs de texte (Text Blocks)

Historiquement, travailler avec du texte littéral multilignes a toujours été fastidieux en Java : les chaînes de caractères littérales sur plusieurs lignes nécessitaient un enchevêtrement de terminaisons de ligne, de délimiteurs de fin de chaîne et d'opérateurs de concaténation.

Les lignes longues sont très difficiles à lire et la concaténation et peu pratiques, à la fois parce qu'il n'est pas possible de copier/coller uniquement le texte et parce qu'il y a beaucoup de caractères parasites (\n" +) sur chaque ligne.

Si ce texte est de l'HTML, XML, SQL ou autres, il est généralement en plus nécessaire d'échapper certains caractères, ce qui rend la lecture et la maintenance du contenu particulièrement compliquées.

Introduits en preview dans Java 13 et en standard en Java 15, les blocs de texte offrent un moyen simple de fournir de manière prévisible des chaînes de caractères littérales multilignes tout en évitant la plupart des séquences d'échappement.

Les blocs de texte permettent de facilement exprimer dans le code une chaîne de caractères multi-lignes brute sans interpolation de variables ou d'expressions.

Un bloc de texte est une nouvelle forme pour exprimer une chaîne de caractères dans le langage Java en offrant une meilleure expressivité et moins de complexité pour des chaînes multilignes.

Comme les chaînes de caractères littérales et les blocs de texte sont compilés en objet de type java.lang.String, les deux syntaxes peuvent utiliser les mêmes fonctionnalités. Seule la syntaxe diffère pour exprimer une chaîne mono-ligne ou multilignes.

 

6.10.1. La situation historique

Le support des chaînes de caractères littérales en Java a toujours souffert de la façon dont les chaînes de caractères sont définies. Une chaîne littérale commence par un double guillemet et se termine par un double guillemet. Malheureusement la chaîne ne peut s'étendre que sur une seule ligne. Si la chaîne doit tenir sur plusieurs lignes, il faut utiliser le caractère d'échappement de saut de ligne \n, fermer la chaîne et concaténer la ligne suivante.

Cela rend les chaînes de caractères multilignes complexes à écrire et à lire et donc à maintenir.

Historiquement la définition de chaînes de caractères littérale multilignes a toujours été fastidieuse car elle requière l'utilisation sur chaque ligne d'un caractère d'échappement \n et de l'opérateur +.

Certaines valeurs sont donc difficiles à exprimer notamment celles qui concernent des chaînes multilignes.

Exemple :
String html = 
  "<HTML>\n\t<BODY>\n\t\t<H1>\"Java\"</H1> \n\t</BODY>\n</HTML>\n";

ou

Exemple :
String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

L'équivalent sous la forme d'un bloc de texte est :

Exemple ( code Java 14 ) :
String html = """
<HTML>
  <BODY>
    <H1>"Java"</H1>
  </BODY>
</HTML>""";

 

6.10.2. Les solutions proposées

Initialement prévue Java 12, la JEP 326 (Raw String Literals) propose les chaînes brutes littérales (raw-string literals), mais les retours négatifs issus de l'évaluation des versions en early access ont conduits à sa suppression avant sa release. Elle est finalement abandonnée en décembre 2018.

Une nouvelle solution est proposée sous la forme de blocs de texte. Les blocs de texte sont introduits en Java 13 en mode preview, suivi par une seconde preview en Java 14 grâce à plusieurs JEP :

  • JEP 355: Text Blocks : introduit une nouvelle syntaxe pour faciliter la saisie, le formatage et la lecture de chaînes de caractères multilignes. Notamment, il n'est plus nécessaire de concaténer chaque ligne avec un retour chariot et d'échapper la plupart des caractères dont les doubles quotes
  • JEP 368: Text Blocks (Second Preview) : ajoute le support de deux nouvelles séquences d'échappement \s et \<line-terminator> pour faciliter certains cas

Finalement, la JEP 378 de Java 15, définit les blocs de texte comme standard dans la syntaxe du langage Java.

 

6.10.3. La description des blocs de texte

Les blocs de texte facilitent l'utilisation de chaînes de caractères littérales multilignes : il propose une syntaxe littérale pour exprimer des chaînes de caractères multilignes.

Un bloc de texte est une chaîne de caractères littérale multilignes qui évite d'avoir recours à la plupart des séquences d'échappement, formate automatiquement la chaîne de manière prévisible et donne le contrôle sur l'indentation.

Les blocs de texte possèdent plusieurs buts :

  • simplifier la saisie de chaînes de caractères multilignes lignes dans le code source, en évitant les séquences d'échappement dans les cas courants
  • améliorer la lisibilité des chaînes dans le code source notamment si la chaîne contient du code formaté comme HTML, XML, JSon, ...
  • assurer une compatibilité avec les chaînes littérales

Un bloc de texte est une nouvelle forme pour exprimer une chaîne de caractères littérale dans le langage Java en offrant une meilleure expressivité et moins de complexité pour des chaînes multilignes.

Les blocs de texte n'introduisent pas un nouveau type mais uniquement une nouvelle syntaxe qui sera traitée pour le compilateur pour obtenir un objet de type java.lang.String. Cela permet d'utiliser un bloc de texte partout où une chaîne de caractères littérale peut être utilisée.

Résultat :
jshell> System.out.println("""
   ...>               <HTML>
   ...>                 <BODY>
   ...>                   <H1>"Java"</H1>
   ...>                 </BODY>
   ...>               </HTML>""");
<HTML>
  <BODY>
    <H1>"Java"</H1>
  </BODY>
</HTML>

Le compilateur supprimera les espaces accessoires, par exemple ceux utilis&s pour aligner le contenu. Cette gestion des espaces pour l'indentation justifie le nom de bloc de texte plutôt que chaîne littérale multilignes.

Quelques exemples simples :

Résultat :
String message = """
   ...> Bonjour""";
message ==> "Bonjour"

jshell> String message = """
   ...>     Bonjour""";
message ==> "Bonjour"

jshell> String message = """
   ...> Bonjour
   ...> """;
message ==> "Bonjour\n"

jshell> String message = """
   ...>     Bonjour
   ...> """;
message ==> "    Bonjour\n"

jshell> String message = """
   ...> Bonjour
   ...>     """;
message ==> "Bonjour\n"

jshell> String message = """
   ...>     Bonjour
   ...>     """;
message ==> "Bonjour\n"

jshell> String message = """
   ...>     Bonjour
   ...>     Et bienvenue""";
message ==> "Bonjour\nEt bienvenue"

Plusieurs points sont remarquables dans ces exemples :

  • les blocs de texte sont transformés en chaînes de caractères littérales
  • le positionnement du délimiteur de fin à une incidence pour avoir une ligne vide ou pas mais aussi sur l'indentation

 

6.10.4. Les avantages

Les blocs de texte permettent de créer facilement des chaînes de caractères multilignes. Ils sont aussi beaucoup plus simples à lire car basiquement ils ne requièrent aucune séquence d'échappement.

Les blocs de texte facilitent la saisie et la lecture du contenu d'une chaîne de caractères multilignes notamment car elle n'oblige pas à ajouter un \n, une double quote et un opérateur de concaténation + comme le requière les chaînes de caractères littérales historiques.

Les blocs de texte offrent une solution différente. Dans un bloc de texte, tout ce qui se trouve entre le délimiteur de début et de fin fait partie de la chaîne, y compris les retours chariots.

Un des avantages avec les blocs de texte c'est que syntaxiquement c'est plus simple et beaucoup plus lisible : un seul délimiteur de début et de fin et le contenu n'est pas pollué car des caractères d'échappement.

 

6.10.5. La syntaxe

La syntaxe est proche de celle utilisée par Python.

Un bloc de texte peut contenir zéro ou plusieurs caractères entourés par un délimiteur de début et de fin. La syntaxe repose sur deux délimiteurs particuliers distincts :

  • délimiteur de début : trois double quotes """ et un retour chariot qui est obligatoire
  • délimiteur de fin : trois double quotes """

L'utilisation de trois doubles quotes d'affilés comme délimiteur a été choisie pour facilement l'identification d'un bloc de texte.

Les blocs de texte débutent par un délimiteur de début qui est composé de plusieurs éléments :

  • 3 caractères double quotes qui se suivent : """
  • zéro ou plusieurs caractères espaces
  • un retour chariot

Le retour chariot ne fait pas parti du contenu du bloc de code. Le contenu du texte débute après le retour chariot du délimiteur de début.

Le délimiteur de début peut être suivi d'aucun ou plusieurs espaces et d'un retour chariot. Un bloc de texte ne peut donc pas être exprimé sur une seule ligne.

Résultat :
jshell> String chaine = """" """";
|  Error:
|  illegal text block open delimiter sequence, missing line terminator
|  String chaine = """" """";
|                     ^

jshell> String chaine = """""""";
|  Error:
|  illegal text block open delimiter sequence, missing line terminator
|  String chaine = """""""";
|                     ^

jshell> String chaine = """"test"""";
|  Error:
|  illegal text block open delimiter sequence, missing line terminator
|  String chaine = """"test"""";
|                     ^

La raison principale est qu'un bloc de texte est essentiellement destiné à être utilisé pour représenter des chaînes de caractères littérales multilignes. Une autre raison est que l'obligation d'avoir un retour chariot facilite la détermination des espaces accessoires.

Contrairement aux chaînes de caractères littérales, un bloc de texte peut contenir :

  • des retours chariots
  • des caractères double quotes qui n'ont pas besoin d'être échappés
  • des caractères échappés incluant \n même si ce dernier n'est pas nécessaire ou recommandé

Un bloc de texte se termine par le délimiteur de fin qui est composé 3 caractères double quotes qui se suivent : """

Le contenu du texte se termine au caractère précédent le délimiteur de fin.

Le délimiteur de fin peut être soit dans la dernière ligne du contenu, soit sur sa propre ligne (dans ce dernier cas, la chaîne se termine par une nouvelle ligne).

Si le délimiteur de fin est sur une ligne dédiée, alors une ligne vide est ajoutée au contenu du bloc de texte.

Exemple ( code Java 15 ) :
System.out.println("""
ligne 1
ligne 2
ligne 3
""");

Est équivalent à :

Exemple :
System.out.println("ligne 1\nligne 2\nligne 3\n");

Ou à :

Exemple :
System.out.println("ligne 1\n" +
"ligne 2\n" +
"ligne 3\n");

Si un retour chariot n'est pas nécessaire à la fin du bloc de texte alors le délimiteur de fin peut être placé à la fin de la dernière ligne du contenu.

Exemple ( code Java 15 ) :
"""
ligne 1
ligne 2
ligne 3"""

Le bloc de texte ci-dessous est équivalent à la chaîne de caractères.

Exemple ( code Java 15 ) :
"ligne 1\nligne 2\nligne 3"

 

6.10.6. La gestion de l'indentation accessoire et essentielle

Il est possible d'aligner le contenu du bloc detexte avec une indentation accessoire. Les blocs de texte seront généralement indentés en fonction du code adjacent et cette indentation n'a qu'une utilité pour faciliter la lisibilité. Cependant, le contenu lui-même peut être indenté de manière significative voire même essentiel comme par exemple pour des données au format JSON ou YAML.

Exemple ( code Java 15 ) :
String personne = """
    {
        nom: "Dupond",
        prenom: "Martin",
        taille: 175
    }
    """;

Dans cet exemple, la première indentation qui concerne toutes les lignes du contenu n'a qu'un rôle accessoire. Par contre, l'indentation des trois propriétés permet de formater les données.

L'indentation peut utiliser des espaces et des tabulations (\t) qui sont considérées comme des espaces. Les espaces et les tabulations non échappées utilisés dans l'indentation sont traités de deux manières par le compilateur :

  • accessoire : les espaces et les tabulations qui la composent sont supprimés par le compilateur car ils sont considérés insignifiants puisqu'utilisés pour faciliter la lisibilité
  • essentiel : les espaces et les tabulations significatifs qui la composent sont conservés

L'indentation d'une partie des lignes est considérée comme importante et donc préservée, mais l'indentation partagée par toutes les lignes est supprimée.

Le compilateur prend en charge de déterminer les espaces accessoires : ils sont ignorés par le compilateur qui les retire. Dans les blocs de texte, le caractère qui n'est pas un espace le plus à gauche de l'une des lignes ou le délimiteur de fermeture le plus à gauche définit le début de l'espace des espaces significatifs.

Exemple ( code Java 15 ) :
String html = """
~~~~~~~~~~~~~~<HTML>
~~~~~~~~~~~~~~  <BODY>
~~~~~~~~~~~~~~    <H1>"Java"</H1>
~~~~~~~~~~~~~~  </BODY>
~~~~~~~~~~~~~~</HTML>""";

Dans l'exemple ci-dessous, les espaces signalés avec un caractère tilde "~" sont considérés comme accessoires et sont donc retirés par le compilateur

Résultat :
jshell> String html = """
   ...>               <HTML>
   ...>                 <BODY>
   ...>                   <H1>"Java"</H1>
   ...>                 </BODY>
   ...>               </HTML>""";
html ==> "<HTML>\n  <BODY>\n    <H1>\"Java\"</H1>\n  </BODY>\n</HTML>"

jshell> System.out.println(html);
<HTML>
  <BODY>
    <H1>"Java"</H1>
  </BODY>
</HTML>

jshell>

Les triples guillemets fermants peuvent être placés sur leur propre ligne : cela permet de contrôler le nombre d'espaces accessoires à retirer mais cela ajoute également une nouvelle ligne à la fin de la chaîne générée.

Si les triples guillemets fermants sont placés directement après la fin du texte, alors il n'est pas possible de contrôler les espaces accessoires. Cela peut cependant être fait explicitement en utilisant la méthode indent() de String pour rajouter une indentation.

Sans mettre le délimiteur sur sa propre ligne, il n'est pas possible de gérer l'indentation. Dans ce cas, il y a deux possibilités :

  • placer le délimiteur de fermeture sur sa propre ligne et supprimer la nouvelle ligne de la chaîne de caractères produite par le compilateur
  • placer le délimiteur de fermeture sur la dernière ligne de contenu et ajouter l'indentation en modifiant la chaîne de caractères produite par le compilateur

Les développeurs peuvent choisir de ne pas supprimer tout ou partie des espaces en tête en utilisant le positionnement du délimiteur de fin. Pour indenter toutes les lignes, il faut :

  • déplacer le contenu vers la droite
  • utiliser le positionnement du délimiteur de fin : en le déplaçant vers la gauche sur une ligne dédiée

Pour marquer certains espaces comme essentiels afin qu'ils ne soient pas supprimés, il est possible de positionner le délimiteur de fin à la position souhaitée. La position du délimiteur de fin peut donc être utilisée de manière significative pour indiquer le début des espaces significatifs que le compilateur doit prendre en compte. Déplacer le délimiteur de fin vers la gauche ou le contenu vers la droite a le même effet : une indentation est incluse dans la chaîne de caractères créée par le compilateur.

Exemple ( code Java 15 ) :
String html = """
              <HTML>
                <BODY>
                  <H1>"Java"</H1>
                </BODY>
              </HTML>
          """;

Ainsi la détermination des espaces accessoires déterminés par le compilateur change.

L'inconvénient de cette solution est qu'elle ajoute une ligne vide à la fin de la chaîne.

Résultat :
jshell> String html = """
   ...>               <HTML>
   ...>                 <BODY>
   ...>                   <H1>"Java"</H1>
   ...>                 </BODY>
   ...>               </HTML>
   ...>           """;
html ==> "    <HTML>\n      <BODY>\n        <H1>\"Java\"</ ...    </BODY>\n    </HTML>\n"

jshell> System.out.println(html);
    <HTML>
      <BODY>
        <H1>"Java"</H1>
      </BODY>
    </HTML>


jshell>

Pour éviter cette ligne supplémentaire, il est possible d'utiliser la séquence d'échappement \ + retour chariot (depuis Java 14).

Résultat :
jshell> String html = """
   ...>               <HTML>
   ...>                 <BODY>
   ...>                   <H1>"Java"</H1>
   ...>                 </BODY>
   ...>               </HTML>\
   ...>           """;
html ==> "    <HTML>\n      <BODY>\n        <H1>\"Java\"</ ...      </BODY>\n    </HTML>"

Des espaces peuvent également apparaître à la fin des lignes, par inadvertance ou par un copier/coller d'un autre fichier. Ces espaces de fin de ligne sont souvent involontaires et insignifiants. Il est très probable que le promoteur ne s'en soucie pas. De plus, ils ne sont généralement pas visibles dans l'éditeur de code source sauf si une fonctionnalité visuelle permet de les faire apparaître.

Par défaut dans les blocs de texte, les espaces en fin de ligne sont également considérés comme accessoires et seront supprimés. Ceci est fait de manière à ce que le contenu du bloc de texte soit toujours visuellement discernable. Si cela n'était pas fait, un éditeur de texte qui supprime automatiquement les espaces de fin de ligne pourrait modifier de manière invisible le contenu d'un bloc de texte.

Si des espaces de fin doivent être significatifs (comme par exemple deux espaces pour une balise <br /> en Markdown), il faut utiliser la séquence d'échappement octale \040 (caractère ASCII 32, espace blanc) ou \s (depuis Java 14).

Il ne faut surtout utiliser la séquence d'échappement \u0020 car elle est traduite à la lecture du fichier source par le compilateur, et donc avant l'analyse lexicale.

 

6.10.7. L'utilisation des séquences d'échappement

Les blocs de texte proposent un support des séquences d'échappement de la même manière que les chaînes de caractères littérales mais généralement ce n'est pas conseillé car rarement utile.

Il est donc possible d'utiliser des séquences d'échappement dans les blocs de texte comme dans les chaînes littérales. Elles sont interprétées comme elles le sont dans les chaînes de caractères littérales.

Résultat :
jshell> String chaine = """
   ...> hello\nworld"""
chaine ==> "hello\nworld"

jshell> println(chaine)
hello
world

Il n'est cependant pas nécessaire d'échapper les doubles quotes dans le contenu d'un bloc de texte.

Résultat :
jshell> String message = """
   ...>     "Hello" Java""";
message ==> "\"Hello\" Java"

Attention, une double quote ne peut pas être accolée au délimiteur de fin du bloc de texte

Résultat :
jshell> String message = """
   ...>     Hello "Java"""";
|  Error:
|  unclosed string literal
|      Hello "Java"""";
|                    ^

Il faut soit ajouter un espace qui sera retiré comme tout espace en fin de ligne

Résultat :
jshell> String message = """
   ...>     Hello "Java" """;
message ==> "Hello \"Java\""

Soit échapper la double quote

Résultat :
jshell> String message = """
   ...>     Hello "Java\"""";
message ==> "Hello \"Java\""

Comme le délimiteur utilisé pour les blocs de texte est une triple double quotes, il faut échapper cette séquence si elle se trouve dans le contenu d'un bloc de texte. Même s'il est possible d'échapper chacune des doubles quotes, le plus simple est d'échapper uniquement la première double quote.

Résultat :
jshell> String chaine = """
   ...>   test \""" test""";
chaine ==> "test \"\"\" test"

Il faut obligatoirement échapper une séquence composée d'un triple double quote.

Exemple ( code Java 15 ) :
String code = """
    String message = \"""
        Bloc de texte dans un bloc de texte
        \""";
    """;

Pour des raisons de lisibilité, c'est la première double quote qui est échappée mais il est possible d'échapper n'importe lequel des trois doubles quotes.

Résultat :
jshell> String code = """
   ...>     String message = ""\"
   ...>         Bloc de texte dans un bloc de texte
   ...>         "\"";
   ...>     """;
code ==> "String message = \"\"\"\n    Bloc de texte dans  ... c de texte\n    \"\"\";\n"

jshell>

La traduction des séquences d'échappements est la dernière étape du traitement des bloc de texte par le compilateur, il est donc possible de contourner les étapes de normalisation des fins de lignes et de suppression des espaces en utilisant des séquences d'échappement explicites. Par exemple :

Résultat :
jshell> String message = """
   ...>     Lundi   \040
   ...>     Mardi   \040
   ...>     Mercredi\040
   ...>     """;
message ==> "Lundi    \nMardi    \nMercredi \n"

jshell>

Les séquences d'échappement d'un caractère espace sous la forme de la valeur octale 40 (20 en décimal) garantie que les trois lignes conserveront leurs espaces car les séquences ne seront interprétées qu'à la troisième étape du traitement des blocs de texte par le compilateur.

Les séquences d'échappement invalides sont interdites par le compilateur.

Résultat :
jshell> String chaine = """
   ...> hello \
world"""
| 
Error:
| 
illegal escape character
| 
hello \
world"""
|         ^
| 
Error:
| 
reached end of file while parsing
|  hello \ world"""
|                  ^

 

6.10.7.1. Les séquences d'échappement ajoutées en Java 14

La JEP 268 dans Java 14 propose le support de deux nouvelles séquences d'échappement :

  • \<fin-de-ligne> : supprime explicitement l'inclusion du caractère de fin de ligne implicite. Cela revient à concaténer la chaîne et la suivante
  • \s : est remplacé par un caractère espace

Elles peuvent être utilisées pour ajuster le formatage d'un bloc de texte.

La séquence d'échappement \<fin-de-ligne> permet de supprimer le retour chariot de la ligne et ainsi de concaténer la ligne courant avec la suivante.

Résultat :
jshell> String texte = """
   ...>     ligne 1 \
   ...>     ligne 2 \
   ...>     ligne 3""";
texte ==> "ligne 1 ligne 2 ligne 3"

Cela peut permettre de faciliter la lecture d'un bloc de texte dont le contenu est très long.

Cela peut aussi être pratique pour supprimer le retour chariot dans le cas où le délimiteur de fin est utilisé sur une ligne dédiée pour définir l'indentation accessoire.

Résultat :
jshell> String texte = """
   ...>     ligne 1 \
   ...>     ligne 2 \
   ...>     ligne 3 \
   ...>   """;
texte ==> "  ligne 1   ligne 2   ligne 3 "

La séquence d'échappement \<fin-de-ligne> ne peut être utilisé que dans les blocs de texte puisque les chaîne de caractères littérales ne peuvent être que sur une ligne unique.

La séquence d'échappement \s est remplacé par un espace (\040 soit le caractère ASCII 32) qui ne sera pas supprimé. Comme les séquences d'échappement ne sont traduites qu'après la suppression des espaces accessoires, elles peuvent servir de marque pour empêcher la suppression des espaces qui précèdent.

Cela peut par exemple être utilisé pour conserver des espaces à la fin des lignes pour les aligner ou s'assurer qu'elles sont tous la même taille.

Résultat :
jshell> String texte = """
   ...>     ligne 1 \s
   ...>     ligne 2 \s
   ...>     ligne 3 \s""";
texte ==> "ligne 1  \nligne 2  \nligne 3  "

La séquence d'échappement \s est aussi utilisable dans les chaînes de caractères littérales classiques.

Exemple ( code Java 14 ) :
  public static void main(String[] args) {
    String message = "**\s\sHello\s\s**";
    System.out.println(message);
  }
Résultat :
**  Hello  **

 

6.10.8. Les traitements par le compilateur

Pour éviter des traitements coûteux à l'exécution, c'est le compilateur qui retire l'indentation accessoire en tête et en fin des lignes.

Un bloc de texte est une expression constante de type String, tout comme une chaîne de caractères littérale. Cependant, à la différence d'une chaîne littérale, le contenu d'un bloc de texte est traité par le compilateur Java en trois étapes distinctes :

  • les retours chariots présents dans le contenu sont tous transformés en LF (\u000A) pour garantir le portage multi-plateforme du code source
  • les espaces utilisés pour suivre l'indentation du code source Java, sont supprimés par défaut
  • les séquences d'échappement dans le contenu sont interprétées

Les blocs de texte sont enregistrés dans le fichier .class en tant que constantes comme les chaînes de caractères littérales. Rien ne les distinguent dans le bytecode des fichiers .class.

A l'exécution les blocs de textes sont des instances de la classe String comme les chaînes de caractères littérales : il n'est pas possible de les distinguer en dehors du code source.

 

6.10.8.1. L'uniformisation des retours chariots

Une des problématiques à gérer dans les chaînes de caractères multilignes est le ou les caractères utilisés comme fin de lignes dans le code source car ils varient selon le système d'exploitation utilisé. Notamment lors de la modification du code source sur différents systèmes, il est possible d'avoir plusieurs fins de lignes utilisées, ce qui peut amener à des confusions ou des incohérences.

Pour apporter une solution, la première étape de traitements des blocs de texte par le compilateur est de normaliser les fins de lignes : quelques soient les fins de lignes CR (\u000D), CRLF (\u000D\u000A sous Windows), ou LF (\u000A sous Linux) pour les remplacer par LF (\u000A).

Cela permet de garantir d'avoir toujours les mêmes fins de lignes utilisées quel que soit le système sur lequel le code est exécuté.

S'il est nécessaire d'avoir des fins de lignes spécifiques au système d'exploitation, il faut remplacer toutes les fins de lignes \n par la séquence utilisée par le système d'exploitation.

Exemple :
      chaine.replaceAll("\n", System.lineSeparator()) ;

Les séquences d'échappement \n (LF) et \r (CR) ne sont pas interprétées durant cette étape de normalisation.

Résultat :
jshell> String chaine = """
   ...>     ligne 1\r
   ...>     ligne 2\n
   ...>     ligne 3\r
   ...>     """
chaine ==> "ligne 1\r\nligne 2\n\nligne 3\r\n"

jshell>

 

6.10.8.2. La gestion des espaces pour l'indentation et en fin de ligne

Le compilateur traite le bloc de texte pour différencier les espaces accessoires au début et à la fin de chaque ligne, des espaces essentiels.

Le compilateur ne détermine pas les espaces de l'indentation essentielle : il supprime les espaces accessoires et considère tout le reste comme essentiel. Le compilateur supprime la même quantité d'espace à chaque ligne de contenu jusqu'à ce qu'au moins une des lignes comporte un caractère qui ne soit pas un espace dans la position la plus à gauche.

La position du délimiteur d'ouverture n'a aucun effet sur l'algorithme, mais la position du délimiteur de fermeture a un effet si celui-ci est placée sur sa propre ligne. La position des espaces accessoires est déterminée grâce à deux règles :

  • le caractère qui n'est pas un espace le plus à gauche de toutes les lignes
  • ou la position la position du délimiteur final s'il est placé sur une ligne dédiée est qu'il est plus à gauche que le caractère précédent

L'algorithme est composé de plusieurs étapes :

  • découper le bloc de texte en lignes individuelles. Les lignes composées uniquement d'un retour chariot restent des lignes vides
  • ajouter toutes les lignes non vides ou non composées entièrement d'espaces dans un ensemble des lignes déterminantes
  • si la dernière ligne du bloc de texte (celle qui contient le délimiteur de fin) est vide alors elle est ajoutée à l'ensemble des lignes déterminantes car l'indentation du délimiteur de fin peut influencer l'indentation du bloc de texte
  • calculer le nombre d'espace qui compose l'indentation accessoire en comptant le nombre d'espace en tête de chaque ligne et en prenant le nombre minimum.
  • supprimer le nombre d'espaces calculé en tête de chaque ligne non vide
  • supprimer les espaces à la fin de toutes les lignes. Ainsi les lignes qui ne contiennent que des espaces sont conservées en tant que ligne vide

Les séquences d'échappement \b (backspace) et \t (tab) ne sont pas interprétées par l'algorithme : ils seront traités lors de la dernière étape qui interprète les séquences d'échappement.

L'implémentation de cet algorithme est proposé dans la méthode stripIndent() de la classe String pour pouvoir être utilisée dans les traitements selon les besoins.

Le compilateur retire les indentations qui sont partagées sur toutes les lignes de manière accessoire et les supprime. Les autres caractères d'espacement sont considérés comme essentiels et sont conservés.

Pour un comportement correct de l'algorithme, il faut que ce soit tous les mêmes caractères qui soient utilisés dans l'indentation accessoire.

Lors de l'utilisation d'une tabulation, le compilateur la considère comme un seul espace car il ne peut pas savoir combien d'espaces représente une tabulation. Il applique une règle qui traite chaque caractère comme un seul espace.

Ainsi si l'indentation accessoire utilise des espaces et des tabulations, le résultat du retrait de l'indentation ne sera probablement pas celui attendu.

Résultat :
public class TestTextBlock {

  public static void main(String[] args) {
    String message = """
        ligne1
               ligne2
        ligne3""";
    System.out.println(message);
  }
}

Dans l'exemple ci-dessus, la première et la dernière ligne du bloc de code est indentée avec des espaces. La seconde ligne est indentée avec des tabulations. Dans certains éditeurs de texte, selon la représentation des tabulations, ces trois lignes peuvent apparaître alignées.

Résultat :
C:\java>java TestTextBlock
      ligne1
ligne2
      ligne3

C:\java>

Le compilateur javac propose l'option -Xlint:text-blocks pour détecter les problèmes liés aux espaces blancs fortuits dans les blocs de texte. Si l'option est activée alors le compilateur émet un avertissement intitulé "inconsistent white space indentation".

Résultat :
C:\java>javac -Xlint:text-blocks TestTextBlock.java
TestTextBlock.java:4: warning: [text-blocks] inconsistent white space indentation
    String message = """
                     ^
1 warning

Cette option permet également d'activer un autre avertissement, "trailing white space will be removed", qui sera émis s'il y a un espace blanc de fin sur n'importe quelle ligne dans un bloc de texte.

Résultat :
public class TestTextBlock {

  public static void main(String[] args) {
    String message = """
        ligne1
               ligne2
        ligne3    """;
    System.out.println(message);
  }
}
Résultat :
C:\java>javac -Xlint:text-blocks TestTextBlock.java
TestTextBlock.java:4: warning: [text-blocks] inconsistent white space indentation
    String message = """
                     ^
TestTextBlock.java:4: warning: [text-blocks] trailing white space will be removed
    String message = """
                     ^
2 warnings

C:\java>

 

6.10.8.3. Le traitement des séquences d'échappement

Une fois le contenu re-indenté, les séquences d'échappement sont interprétées. Toutes les séquences d'échappement définis dans la JLS utilisables dans les chaînes de caractères sont utilisables dans les blocs de texte.

L'interprétation des séquences d'échappements à l'étape finale permet notamment aux développeurs d'utiliser les séquences \n, \f, et \r pour le formatage vertical d'une chaîne sans que cela n'affecte la normalisation des retours chariot à l'étape 1, et d'utiliser \b et \t pour le formatage horizontal d'une chaîne sans que cela n'affecte la suppression des espaces accessoires à l'étape 2.

Résultat :
jshell> String html = """
   ...>               <HTML>\r
   ...>                 <BODY>\r
   ...>                   <H1>"Java"</H1>\r
   ...>                 </BODY>\r
   ...>               </HTML>""";
html ==> "<HTML>\r\n  <BODY>\r\n    <H1>\"Java\"</H1>\r\n  </BODY>\r\n</HTML>"

Cela permet aussi des préserver des espaces en fin de ligne en utilisant la séquence d'échappement octale \040.

Résultat :
jshell> String couleurs = """
   ...>     rouge \040
   ...>     orange\040
   ...>     vert  \040""";
couleurs ==> "rouge  \norange \nvert   "

 

6.10.8.4. Le résultat de la transformation

Le bytecode généré par le compilateur pour un bloc de texte est une chaîne de caractères de type java.lang.String. Ainsi les valeurs littérales des chaînes de caractères et des blocs de texte sont compilés vers le type java.lang.String.

Dans le bytecode, il n'est donc pas possible de distinguer si le code source utilise un bloc de texte ou une chaîne de caractères littérale.

Comme toutes les chaînes de caractères littérales, celles générés par le compilateur à partir de bloc de texte sont ajoutées dans le pool de String. Les mêmes chaînes de caractères exprimées en littérales et en bloc de texte seront donc égales chaine1.equals(chaine2) retourne true et grâce à la gestion interne des chaînes dans la JVM, chaine1==chaine2 retourne également true.

Résultat :
jshell> String chaine1 = "test";
chaine1 ==> "test"

jshell> String chaine2 = """
   ...> test""";
chaine2 ==> "test"

jshell> chaine1==chaine2
$3 ==> true

 

6.10.9. La concaténation et le formatage de blocs de texte

Les blocs de texte peuvent être concaténés avec des chaînes de caractères (objets de type String ou valeurs littérales) et vice versa, puisque dans le byte code se sont des instances de type java.lang.String.

Résultat :
jshell> String message = """
   ...> Bonjour""" + ",  bienvenue"
message ==> "Bonjour,  bienvenue"

jshell>

La concaténation avec une chaîne de caractères peut par exemple être nécessaire pour insérer une valeur.

Résultat :
jshell> String nom = "JM"
nom ==> "JM"

jshell> String message = """
   ...> Bonjour """ + " " + nom + """
   ...> , bienvenue"""
message ==> "Bonjour JM, bienvenue"

jshell>

Cependant la syntaxe à utiliser n'est dans ce cas pas forcement la plus simple ni la plus lisible. e plus, les blocs de texte ne proposent pas de support pour l'interpolation et la concaténation. Il est cependant possible d'utiliser les méthodes replace() ou format() de la classe String.

Résultat :
jshell> String message = """
   ...> Bonjour $nom,
   ...>  bienvenue""".replace("$nom","JM")
message ==> "Bonjour JM,\n bienvenue"

jshell>

jshell> String message = String.format("""
   ...> Bonjour %s,
   ...>  bienvenue""", "JM")
message ==> "Bonjour JM,\n bienvenue"

jshell>

Il est aussi possible d'utiliser la méthode d'instance formatted() de la classe String ajoutée en Java 15.

Résultat :
jshell> String message = """
   ...> Bonjour %s,
   ...>  bienvenue""".formatted(nom)
message ==> "Bonjour JM,\n bienvenue"

jshell>

 

6.10.10. Les méthodes dans la classe String

Trois nouvelles méthodes en relation avec les blocs de texte ont été ajoutées à la classe String.

Méthode

Rôle

String formatted​(Object... args)

Formater la chaîne qui contient le format en substituant les motifs inclus par les valeurs fournies en paramètres (depuis Java 15)

String stripIndent()

Renvoyer la chaîne elle-même dont l'indentation accessoire et de fin de ligne est retirée (depuis Java 15)

String translateEscapes()

Renvoyer la chaîne elle-même dont certaines séquences d'échappement sont traduites (depuis Java 15)


En Java 13, ces 3 méthodes sont ajoutées en étant dépréciées forRemoval=true pour indiquer que ces méthodes pourraient être retirées si les blocs de texte sont retirés pendant leur évaluation en mode preview.

En Java 14, ces 3 méthodes ne sont plus dépréciées mais elles sont annotées avec jdk.internal.PreviewFeature. Les API annotées avec cette annotation sont considérées comme les fonctionnalités du langage en mode preview puisque c'est la raison de leur ajout. Leur mise en oeuvre doit donc respecter les règles d'activation pour utilisation des fonctionnalités en mode preview.

Résultat :
C:\java>type TestString.java
public class TestString {

  public static void main(java.lang.String[] args) {
    String tab = "\\t".translateEscapes();
  }
}
C:\java>javac TestString.java
TestString.java:4: error: translateEscapes() is an API that is part of a preview feature
    String tab = "\\t".translateEscapes();
                      ^
1 error

C:\java>javac --enable-preview --source 14 TestString.java
Note: TestString.java uses preview language features.
Note: Recompile with -Xlint:preview for details.

C:\java>java TestString
Erreur : LinkageError lors du chargement de la classe principale TestString
       java.lang.UnsupportedClassVersionError: Preview features are not enabled for TestString
(class file version 58.65535). Try running with '--enable-preview'

C:\java>java --enable-preview TestString

C:\java>

 

6.10.10.1. La méthode translateEscapes

La méthode translateEscapes() permet de traduire les séquences d'échappement contenues dans la chaîne de caractères.

Séquence d'échappement

Traduction

\b

U+0008

\t

U+0009

\n

U+000A

\f

U+000C

\r

U+000D

\s

U+0020

\"

U+0022

\'

U+0027

\\

U+005C

\0 à \377

Caractères équivalent à la valeur octale

\<fin-de-ligne>

Suppression de la fin de ligne


Cette méthode ne traduit pas les séquences d'échappement avec une valeur Unicode (\uNNNN).

Résultat :
jshell> String retourChariot = "\\n".translateEscapes();
retourChariot ==> "\n"

jshell>

Comme la méthode est initialement associée à la fonctionnalité des blocs de texte en preview :

  • en Java 13, cette méthode est dépréciée forRemoval=true
  • en Java 14, elle est marquée comme étant associée à une fonctionnalité en preview
  • en Java 15, elle est standard

 

6.10.10.2. La méthode formatted

La méthode formatted(Object... args) est une méthode d'instance qui est similaire à la méthode statique format() en lui passant en paramètres la chaîne elle-même et args. L'avantage est qu'il est possible de l'invoquer sur une valeur littérale sous la forme d'une chaîne de caractères ou d'un bloc de texte et même d'être chainée avec d'autres méthodes de classe String.

Résultat :
jshell> String message = """
   ...>     Reference : %s
   ...>     Prix : %.2f euros
   ...>     """.formatted("56789", 123.45);
message ==> "Reference : 56789\nPrix : 123,45 euros\n"

jshell>

Comme la méthode est initialement associée à la fonctionnalité des blocs de texte en preview :

  • en Java 13, cette méthode est dépréciée forRemoval=true
  • en Java 14, elle est marquée comme étant associée à une fonctionnalité en preview
  • en Java 15, elle est standard

 

6.10.10.3. La méthode stripIndent

La méthode stripIndent() permet de retirer l'indentation accessoires en début et fin de lignes dans une chaîne de caractères multi-lignes en utilisant le même algorithme que celui utilisé par le compilateur pour traiter les blocs de texte.

Résultat :
jshell> String code = "   ligne 1\n   ligne 2\n   ligne 3";
code ==> "   ligne 1\n   ligne 2\n   ligne 3"

jshell> System.out.println(code);
   ligne 1
   ligne 2
   ligne 3

jshell> System.out.println(code.stripIndent());
ligne 1
ligne 2
ligne 3

jshell>

Comme la méthode est initialement associée à la fonctionnalité des blocs de texte en preview :

  • en Java 13, cette méthode est dépréciée forRemoval=true
  • en Java 14, elle est marquée comme étant associée à une fonctionnalité en preview
  • en Java 15, elle est standard

 

6.10.11. Les cas d'utilisation

Les blocs de texte facilitent grandement la représentation sous la forme de chaînes de caractères littérales de code source (Java, JavaScript, SQL, ...) ou de données formatées (XML, JSON, HTML, ...) dans le code source Java.

Exemple ( code Java 15 ) :
String html = """
    <html>
      <body>
        <p class="titre">Hello world</p>
      </body>
    </html>""";

Historiquement, la saisie et la lecture d'un document Json dans une chaîne de caractères littérale sont compliquées car il faut en plus échapper les doubles quotes.

Exemple :
    String json = "    {\n" + 
        "      \"nom\": \"Dupond\",\n" + 
        "      \"prenom\": \"Pierre\",\n" + 
        "      \"id\": 1234\n" + 
        "    }";

L'utilisation des blocs de texte facilite la saisie et la lecture de documents Json.

Exemple ( code Java 15 ) :
String json = """
    {
      "nom": "Dupond",
      "prenom": "Pierre",
      "id": 1234
    }""";

Les blocs de texte facilitent aussi l'incorporation de requêtes SQL en limitant les risques d'erreur de saisie qui pourraient engendrer des requêtes invalides.

Exemple ( code Java 15 ) :
String sql = """
    SELECT id, nom, prenom
    FROM utilisateur
    WHERE id = ? """;

Il est possible d'utiliser les blocs de texte pour facilement inclure du code source Java dans une chaîne de caractères.

Exemple ( code Java 15 ) :
String source = """
         String message = "Hello world";
         System.out.println(message);
    """;

Les blocs texte peuvent aussi faciliter l'insertion d'expressions régulières qui requièrent souvent d'échapper les caractères spéciaux qu'elles contiennent.

 

6.10.12. Les bonnes pratiques

Un bloc de texte peut être utilisé pour représenter une chaîne de caractères vide mais cela n'est pas recommandé car sa représentation nécessite deux lignes.

Exemple ( code Java 15 ) :
String chaine = """
""";

C'est la même chose pour une chaîne qui ne contient qu'une seule ligne

Exemple ( code Java 15 ) :
String chaine = """
test""";

Dans les deux cas, il est préférable d'utiliser des chaînes de caractères littérales plutôt que des blocs de texte.

Il est préférable de réserver l'utilisation des caractères d'échappement lorsque cela améliore la lisibilité.

Exemple ( code Java 15 ) :
String csv = """
    Nom;Adresse;Ville
    Alain Dupond;"23 rue de la paille\nAppartement 12";Paris
    Sophie Durand;"9 rue de la tour\nRésidence Victor Hugo";Lyon
""";

Il n'est pas conseillé d'aligner le contenu sur le début du délimiteur de fin car cela implique de décaler toutes les lignes si nom de la variable change.

Exemple ( code Java 15 ) :
String chaine = """
                ligne1
                ligne2
                ligne3""";

Il est recommandé d'utiliser sa propre indentation accessoire.

Exemple ( code Java 15 ) :
String chaine = """
      ligne1
      ligne2
      ligne3""";

Il est recommandé d'utiliser la séquence d'échappement \<fin_de_ligne> pour éviter la ligne vide lorsque le délimiteur de fin est sur sa propre ligne.

Exemple ( code Java 15 ) :
String chaine = """
      ligne1
      ligne2
      ligne3\
  """;

Il n'est pas recommandé de mixer l'utilisation d'espaces et de tabulations dans l'indentation d'un bloc de texte

Il n'est pas recommandé d'utiliser un bloc de texte directement dans une expression complexe comme une expression Lambda. Il est préférable de définir le bloc de texte dans une variable locale et d'utiliser cette variable dans l'expression.

 

 


[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

78 commentaires Donner une note à l´article (5)

 

Copyright (C) 1999-2022 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse : GNU Free Documentation Licence.