Les chaînes sont des suites de caractères (par exemple: "hello"). Java ne contient pas de type chaîne prédéfini. En revanche, la bibliothèque Java standard contient une classe prédéfinie appelée String. Chaque chaîne est un objet (une référence) de la classe String. Comme toute déclaration d'une variable objet, l'instruction :
String ch;
déclare que ch est destinée à contenir une référence à un objet de type String. Par ailleur, la notation "bonjour" désigne en fait un objet de type String (ou, pour être plus précis, sa référence), créé automatiquement par le compilateur. Ainsi, avec :
ch = "bonjour";
on abouti à une situation qu'on peut schématiser ainsi :
La classe String dispose de deux constructeurs, l'un sans argument créant une chaîne vide, l'autre avec un argument de type String qui crée une copie :
Il ne faut pas perdre de vue qu'on manipule constamment des références à des objets et que celles-ci peuvent voir leur valeur évoluer au fil du programme. Considérons l'instruction suivante :
Après l'exécution, nous obtenons la situation de droite
Exécutons maintenant ces instructions :
ch = ch1;
ch1 = ch2;
ch2 = ch;
Nous obtenons une permutation des références ch1 et ch2, par contre les deux objets de type chaîne n'ont pas été modifiés.
Java autorise l'emploi du signe + et += pour joindre (concaténer) deux chaînes.
Lorsque vous concaténez une chaîne et une valeur qui n'est pas une chaîne, cette valeur est convertie en chaîne.
Il existe un certain nombre de méthodes intégrées à la classe String pour permettre différentes manipulations sur les chaînes :
Attention : Les objets de la classe String sont inaltérables. Il n'existe donc pas de méthodes permettent de modifier un caractère dans une chaîne existante. Il faut alors utiliser une sous-chaîne.
Le fait que le compilateur construise un objet de type String lorsqu'il rencontre une constante chaîne (entre guillemets) signifie que vous pouvez parfaitement utiliser les méthodes de String avec cette chaîne. Vous pouvez écrire :
n = "Monsieur".length(); // n est initialisé avec la taille de la chaîne (ici 8)
Attention : N'employez pas l'opérateur == pour tester l'égalité de deux chaînes! Cet opérateur détermine seulement si les chaînes sont stockées au même emplacement (même référence - adresse de la variable). Il est évident que si deux chaînes se trouvent à la même adresse, elles doivent être égales. Mais des copies de chaînes identiques peuvent être stockées à des emplacements différents dans la mémoire.
La méthode equals
Fort heureusement, la classe String dispose d'une
méthode equals qui compare le contenu de
deux chaînes et renvoie true si elles sont
égales, et false dans le cas contaire.
La méthode equalsIgnoreCase
La méthode equalsIgnoreCase effectue la
même comparaison, mais sans distinguer les majuscules et les minuscules.
La méthode toLowerCase crée une nouvelle chaîne en remplaçant toutes les majuscules par leur équivalent en minuscules (lorsque celui-ci existe). La méthode toUpperCase crée une nouvelle chaîne en remplaçant toutes les minuscules par leur équivalent en majuscules.
Conversion d'un type primitif en une chaîne
Nous avons déjà vu comment l'opérateur "+" effectuait un "formatage" en convertissant n'importe quel type primitif en une chaîne. Vous pouvez effectuer directement de telles conversions, en utilisant la méthode valueOf de la classe String.
La méthode valueOf peut recevoir un argument de type quelconque et pas seulement de type entier.
Conversion d'une chaîne en un type primitif
On peut réaliser les conversions inverses des précédentes. Il faut alors recourir à une méthode de la classe enveloppe associée au type primitif : Integer pour int, Double pour double, etc...
Par exemple, pour convertir une chaîne en un entier de type int, on utilisera la méthode statique parseInt de la classe enveloppe Integer, comme ceci :
D'une manière générale, nous disposons des méthodes pour chaque type primitif comme cela est précisé ci-dessus.
Les chaînes Java sont implémentées sous forme de suites de valeurs char. Comme nous l'avons vu, le type char est une unité de code permettant de représenter des points de code Unicode en codage UTF-16. Les caractères Unicode les plus souvent utilisés peuvent être représentés par une seule unité de code. Les caractères complémentaires exigent, quant à eux, une paire d'unité de code.
String chaîne = "Bonjour";
int longueur = chaîne.length(); // longueur vaut 7
int pointsCode = chaîne.codePointCount(0, chaîne.length());
char premier = chaîne.charAt(0); // le premier est 'B'
char dernier = chaîne.charAt(6); // le premier est 'r'
int index = chaîne.offsetByCodePoints(0, n);
int dernier = chaîne.codePointAt(index);
Les tableaux permettent de regrouper une suite de variables de même type. Chaque élément du tableau est une variable que vous pouvez utiliser comme n'importe quelle variable de ce type. Il est possible de définir des tableaux pour les types primaires ou les classes. Cependant, tous les éléments doivent être du même type. En Java, les tableaux sont des objets. Pour pouvoir utiliser un tableau, vous devez respecter les trois étapes suivantes :
Considérons cette déclaration :
int t[];
Elle précise que t est destiné à contenir la référence à un tableau d'entiers. Vous constatez qu'aucune dimension ne figure dans cette déclaration et, pour l'instant, aucune valeur n'a été attribuée à t.
On crée un tableau comme on crée un objet, c'est à dire en utilisant l'opérateur new. On précise à la fois le type des éléments, ainsi que leur nombre (dimension du tableau).
t = new int[5]; // t fait référence à un tableau de 5 entiers
Cette instruction alloue l'emplacement nécessaire à un tableau de 5 éléments de type int et place la référence dans t. Les 5 éléments sont initialisés par défaut (comme tous les champs d'un objet) à une valeur nulle (0 pour int).
La déclaration d'une référence à un tableau précise donc simplement le type des éléments du tableau. Elle peut prendre deux formes différentes.
Création par l'opérateur new
Après avoir déclaré la variable tableau, il faut allouer les éléments de ce tableau. On utilise pour ce faire l'opérateur new. Vous avez déjà mis en oeuvre cet opérateur pour créer des objets. A ce niveau, il est obligatoire de préciser le nombre d'éléments du tableau. Ces derniers sont initialisés automatiquement en fonction du type de tableau (0 pour les numériques, '\0' pour les caractères, false pour les boolean et null pour tout le reste).
Utilisation d'un initialiseur
Lors de la déclaration d'une référence, il est possible de fournir la valeur à chacune des cases du tableau. Il suffit de donner la liste entre accolades sans utiliser l'opérateur new. Java se sert du nombre de valeurs figurant dans l'initialiseur pour en déduire la taille du tableau à créer.
Accès individuel aux éléments d'un tableau
On peut manipuler un élément de tableau comme on le ferait avec n'importe quelle variable ou n'importe quel objet de ses éléments. On désigne un élément particulier en plaçant entre crochets, à la suite du nom du tableau, une expression entière nommée indice indiquant sa position. Le premier élément correspond à l'indice 0 (et non 1).
Affectation de tableaux
Java permet aussi de manipuler globalement des tableaux, par le biais d'affectations de leurs références.
Exécutons maintenant l'affectation :
t1 = t2; // la référence contenue dans t2 est recopiée dans t1
Nous
aboutissons à la situation présentée ci-contre. Dorénavant, t1 et t2 désigne
le même tableau. Ainsi, avec :
t1[1] = 5;
System.out.println (t2[1]);
on obtiendra l'affichage de la valeur 5, et non 11.
Si l'objet que constitue le tableau de trois entiers anciennement désigné par t1 n'est plus référencé par ailleurs, il deviendra candidat au ramasse-miettes.
Il est très important de noter que l'affectation de références de tableaux n'entraine aucune recopie des valeurs des éléments du tableau. On retrouve exactement le même phénomène que pour l'affectation d'objets.
Pour connaître le nombre d'éléments d'un tableau, vous pouvez utiliser l'attribut public length. Cet attribut peut être utilisé pour tous les types de tableaux. Considérez le code suivant :
int tab[] = new int [10];
int taille = tab.length; // taille est égal à 10
Après avoir déclaré un tableau, utilisez length pour récupérer le nombre d'éléments du tableau (ici 10).
Le mot length désigne en quelque sorte un attribut du tableau, attribut que l'on peut d'ailleurs uniquement lire ; ce n'est pas un attribut au même titre que les attributs d'une classe. Remarquons qu'il n'y a pas d'autre attribut pour une variable de type tableau.
Attention : Pour définir un tableau d'objets, il faut commencer par définir un tableau de références, puis, pour chaque référence, créer l'objet associé. Un tableau d'objets est en fait un tableau de références, et chaque référence pointe vers l'objet associé.
Le résultat est le suivant :
Point : 1, 2
Point : 4, 5
Point : 8, 9
Il est possible d'avoir une écriture plus concise de la classe TabPoint en utilisant l'initialiseur de tableau.
Lorsqu'on transmet un nom de tableau en argument d'une méthode, on transmet en fait (une copie) la référence au tableau. La méthode agit directement sur le tableau concerné et non sur une copie. Ce principe est d'ailleurs utilisé pour tous les types d'objets. D'une façon générale, lorsqu'on transmet un objet en argument d'une méthode, on transmet toujours sa référence.
Le résultat est :
Salut
bonjour
Hello
*
*
*
Il est même possible d'initialiser un tableau anonyme :
new int[] { 2, 3, 5, 7, 11, 13 }
Cette expression alloue un nouveau tableau et le remplit avec les valeurs spécifiées entre parenthèses. Elle détermine le nombre de valeurs fournies et donne au tableau le même nombre d'éléments. Cette syntaxe est employée lorsqu'on désire passer un tableau à une méthode sans créer pour cela une variable locale. L'exemple :
printLabels(new String[] { "Région", "Ventes" });
est un raccourci pour :
String[] titres = { "Région", "Ventes"};
printLabels(titres);
Il existe une méthode très très simple pour afficher toutes les valeurs d'un tableau dans la sortie standard, et ce grâce à la méthode toString() de la classe Arrays. L'appel Arrays.toString(tableau) renvoie une chaîne de caractères contenant les éléments du tableau, insérés entre crochets et séparés par des virgules :
System.out.println(Arrays.toString(tableau)); // affichepar exemple à l'écran "[2, 3, 5, 7, 11, 13]"
Il est possible de copier une variable tableau dans une autre, mais attention, comme nous l'avons vu plus haut, les deux variables feront alors référence au même tableau :
int[ ] suite = {17, 19, 23, 29, 31, 37};
int[ ] entier = suite;
entier[3] = 12; // suite[3] vaut maintenant 12
int[ ] suite = {17, 19, 23, 29, 31, 37};
int[ ] entier = Arrays.copyOf(suite, suite.length);
int[ ] suite = {17, 19, 23, 29, 31, 37};
suite = Arrays.copyOf(suite, 2 * suite.length);
Les autres éléments sont remplis de 0 si le tableau contient des nombres, false si le tableau contient des valeurs booléennes. A l'inverse, si la longueur est inférieure à celle du tableau initial, seules les valeurs initiales sont copiées.
Si vous voulez trier un tableau de nombres, utilisez une des méthodes sort() de la classe Arrays :
int[ ] nombre = new int[100];
...
Arrays.sort(nombre);
Cette méthode utilise une version adaptée de l'algorithme QuickSort qui se révèle très efficace sur la plupart des ensembles de données.
§
Exercices |
Soit la déclaration suivante dans la méthode main :
|
|
|
Récupération des sources des corrections