Java Structure de Controle
Java Structure de Controle
Structures de contrôle
Dans le corps d’une méthode, il y a trois manières fondamentales
d’enchaîner les actions : la séquence, la boucle et la décision. Ce chapitre introduit
les énoncés de base Java qui permettent d’exprimer ces trois types
d’enchaînement.
3.1 La séquence
Les exemples vus jusqu’à présent ont utilisé une séquence. Une séquence
d’énoncés est de la forme générale suivante :
Les énoncés sont placés en séquence les uns après les autres et sont exécutés
dans cet ordre. La Figure 1 montre une représentation graphique d’une
séquence avec un diagramme d’activité UML. Un rectangle aux coins arrondis
représente une activité. Dans notre cas, une activité correspond à un énoncé
Java. Les flèches indiquent l’ordre d’exécution des activités. Le point noir
représente le début et le point noir encerclé la fin de l’exécution.
énoncé 1
énoncé 2
...
énoncé n
Le corps d’une méthode est essentiellement un bloc Java. Il est à noter qu’il
est permis d’avoir un seul énoncé dans le bloc. Les différents types d’énoncés
seront étudiés en détails. Jusqu’à présent, nous avons rencontrés trois sortes
d’énoncés : énoncé de déclaration de variable, d’affectation et d’appel de
méthode. Un bloc Java est lui-même considéré comme un énoncé. On
obtient ainsi le diagramme syntaxique suivant :
énoncé :
déclaration de variable
affectation
appel de méthode
bloc d'énoncés
Exemple. JavaPasAPas/chapitre_3/[Link]
Dans l’exemple suivant, les deux énoncés de saisie de chaîne de Exemple1 ont
été regroupés en un bloc qui est imbriqué dans le bloc de la méthode main().
/**
* [Link]
* Modification de Exemple1 avec un bloc imbriqué
*/
import [Link]; // Importe la classe [Link]
public class ExempleBloc{
// Déclaration de variables
String chaine1, chaine2; // Les entiers sous forme de chaînes
int entier1, entier2, somme; // Les entiers à additionner
// Saisir les deux chaînes de caractères qui représentent des nombres entiers
{
chaine1 = [Link]("Entrez un premier nombre entier");
chaine2 = [Link]("Entrez un second nombre entier");
}
63
// Convertir les chaînes en entiers
entier1 = [Link](chaine1);
entier2 = [Link](chaine2);
Exemple. JavaPasAPas/chapitre_3/[Link]
S’il fallait afficher les entiers de 1 à 1 000 000, le programme serait long à
écrire… Et même si vous aviez la patience de le faire, ce serait sans aucun
doute inefficace parce que le processeur devrait charger et interpréter un
64
grand nombre d’instructions redondantes. Par ailleurs, si le nombre de
répétition n’est pas connu au moment d’écrire le code, par exmple si c’est
l’utisateur ou la taille du fichier qui détermine le nombre de répétition, il peut
être tout simplement impossible de procéder avec une répétition explicite
dans le code. Pour éviter de répéter les énoncés dans le programme, on peut
employer une répétition (aussi appelée boucle ou itération). L’énoncé while Java
est un des énoncés Java qui permet d’effectuer une répétition.
Exemple. JavaPasAPas/chapitre_3/[Link]
énoncé while :
L’expression entre parenthèses doit être une expression booléenne, aussi appelée
condition , dont la valeur est vraie (true) ou faux (false). Si cette condition est
respectée (i.e. la valeur retournée par l’expression est true), l’énoncé après le
while est répété en boucle jusqu’à ce que la condition ne soit plus respectée
(i.e. la valeur retournée par l’expression est false).
Il faut prendre soin, lorsque l’on conçoit une boucle, de s’assurer que celle-
ci puisse se terminer : une boucle peut être infinie et ne jamais se terminer. Il
s’agit généralement d’une erreur.
65
La figure suivante illustre l’enchaînement des énoncés du programme par un
diagramme d’activité UML. Un losange représente une condition. Les
flèches qui partent de la condition montrent les deux enchaînements
possibles selon le résultat de la condition. Le diagramme montre bien le
concept de répétition dans l’enchaînement des énoncés.
int compteur = 1;
[ compteur > 5 ]
[ compteur <= 5 ]
[Link](null,"Va
leur du compteur: "+compteur);
compteur =
compteur + 1;
[Link](0);
S’il y a plus d’un énoncé à répéter, comme c’est le cas de notre exemple, il
faut les regrouper en un bloc, donc mettre ces énoncés entre accolades. Il est
à noter que s’il y a un seul énoncé, les accolades sont facultatives. Dans notre
exemple, la condition est
(compteur <= 5){
66
Donc, tant que cette condition s’avère vraie (tant que le compteur sera plus
petit ou égal à 5), les énoncés
[Link](null,"Valeur du compteur:
"+compteur);
compteur = compteur + 1;
s’exécuteront en boucle. Dès que le compteur dépasse la valeur de cinq, ces
énoncés arrêtent de s’exécuter, et le programme passe à l’énoncé suivant, qui
est [Link](0). Ce dernier énoncé met fin au programme.
Expression booléenne
67
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Afficher les valeurs 0,2,4,6,8,10
*/
import [Link];
public class ExerciceWhile1{
public static void main (String args[]) {
int compteur = 0;
while(compteur <= 10){
[Link](null,"Valeur du compteur: "+compteur);
compteur = compteur + 2;
}
[Link](0);
}
}
Exercice. Modifiez l’exemple afin d’afficher 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5.
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Afficher les valeurs de compteur 5,4,3,2,1,0,-1,-2,-3,-4,-5
*/
import [Link];
public class ExerciceWhile2{
public static void main (String args[]) {
int compteur;
compteur = 5;
while(compteur >= -5){
[Link](null,"Valeur du compteur:"+compteur);
compteur = compteur - 1;
}
[Link](0);
}
}
68
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire dix entiers et en afficher la somme avec un while
*/
import [Link];
public class ExerciceWhile3 extends Object {
public static void main (String args[]) {
String serie;
int entier;
int compteur = 1;
int somme = 0;
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire une suite d'entiers jusqu'à ce que l'entier 0 soit entré et afficher la somme
* des entiers lus.
*/
import [Link];
public class ExerciceWhileSentinelle {
public static void main (String args[]) {
String serie;
int somme = 0;
int entier = 1; // N'importe quelle valeur différente de 0 ferait l'affaire
while (entier != 0) {
serie = [Link]("Entrez un nombre");
entier = [Link] (serie);
somme = somme + entier;
}
[Link](null,"La somme de tous les nombres est de " +somme+ ".");
[Link](0);
}
}
69
3.3 Qualité du logiciel, tests et débogage
Un aspect fondamental de la qualité d’un programme est la validité des
résultats produits par rapport à sa spécification. Une pratique courante en
développement de logiciel est de vérifier que les résultats corrects sont
produits pour un ensemble de cas de tests.
Ce genre de test est dit fonctionnel étant donné qu’il vérifie que le
fonctionnement du programme est valide par rapport à ce qu’il doit faire
(spécification fonctionnelle). Dans des programmes plus complexes, d’autres
aspects peuvent aussi être mesurés tel que le temps de calcul, la mémoire
consommée ou d’autres aspects dits non fonctionnels.
Dans l’approche de test par boîte noire ou opaque (black box testing), les tests
sont choisis sans examiner le code lui-même. On cherche à choisir les cas de
tests de manière à produire différentes combinaisons d’input qui couvrent
les différentes possibilités prévues dans la spécification du programme. Dans
70
l’approche de test par boîte blanche ou transparente (white box testing, glass box
testing), les tests sont conçus en tenant compte du code. En particulier, il faut
tenter de parcourir toutes les parties du code par l’ensemble des tests.
Exercice. Vérifiez si les bons résultats sont produits avec les tests précédents
pour la solution du dernier exercice.
Exemple. JavaPasAPas/chapitre_3/[Link]
A noter qu’il n’y a qu’un énoncé à répéter dans notre exemple, et qu’il n’est
pas nécessaire de mettre un bloc pour l’énoncé à répéter. En effet, dans
l’exemple, il n’y a pas d’accolades. Cependant, s’il y avait plusieurs énoncés,
il aurait été nécessaire de mettre des accolades avant et après les énoncés à
répéter.
int compteur = 1;
while(compteur <= 5){
[Link](null,"Valeur du
compteur: "+compteur);
compteur = compteur + 1;
72
}
Exemple. JavaPasAPas/chapitre_3/[Link]
Exercice. Utilisez un for pour lire une série d’entier jusqu’à ce que l’entier 0
soit entré (cas de sentinelle) et afficher la somme de ces entiers.
73
Exercice. Affichez le résultat suivant sur la sortie standard (avec
[Link]() et [Link]())
1
12
123
1234
12345
123456
1234567
12345678
123456789
Solution. JavaPasAPas/chapitre_3/[Link]
Exemple. JavaPasAPas/chapitre_3/[Link]
Le programme suivant lit un entier (unInt) et indique s’il est plus grand que
10 ou non. Pour déterminer le message à afficher, une décision est prise en
comparant l’entier lu à 10 dans une condition (unInt > 10). Selon le résultat
de la condition, le if permet de choisir entre les deux énoncés alternatifs à
exécuter. La première alternative suit la condition et elle est exécutée si la
condition est évaluée à vrai (true). Sinon (i.e. la valeur de l’expression est false),
l’énoncé qui suit l’identificateur else est exécuté.
74
/**
* [Link]
* Petit exemple illustrant l'énoncé if.
*/
import [Link];
public class ExempleIf{
public static void main (String args[]) {
String unString = [Link]("Entrez un premier nombre entier");
int unInt = [Link](unString);
// Exemple d'énoncé if
if (unInt > 10)
[Link](null,unInt + " est plus grand que 10");
else
[Link](null,unInt + " n'est pas plus grand que 10");
[Link](0);
}
}
La syntaxe du if est :
énoncé if :
Lors de l’utilisation d’un if, l’énoncé1 suivant l’expression ne sera exécutée que
si l’expression (la condition) se révèle vraie. L’énoncé ne s’exécute qu’une
seule fois. Si l’expression se révèle fausse, l’énoncé2 suivant le mot else sera
exécuté (une seule fois). Dans notre exemple, si la condition
(unInt > 10)
se révèle vraie, l’énoncé
[Link](null,unInt + " est plus
grand que 10");
sera exécuté. Si l’expression se révèle fausse, l’énoncé qui sera exécuté est
[Link](null,unInt + " n'est
pas plus grand que 10");
75
[ unInt > 10 ] if [ unInt <= 10 ]
[Link](null, [Link](null,
unInt + " est plus grand que 10"); unInt + " n'est pas plus grand que 10");
[Link](0);
Il est à noter que s’il y a plusieurs énoncés à exécuter dans la partie énoncé1(cas
vrai) ou énoncé2 (cas faux), il faut les regrouper en un bloc, se débutant par
l’accolade ouvrante et se terminant par l’accolade fermante.22 Aussi, la partie
else est optionnelle. En son absence, lorsque l’expression de condition est
fausse, rien n’est exécuté. Ceci peut causer une ambigüité potentielle illustrée
par l’exemple suivant.
Exemple. JavaPasAPas/chapitre_3/[Link]
// If ambigu
if (entier1 > 10)
if (entier2 > 10)
[Link](null,entier1 + " et "+ entier2 + " sont plus grands que 10");
else
[Link](null,entier1 + " est inférieur ou égal à 10");
[Link](0);
}
}
Pour forcer le else à être associé au premier if, il faut utiliser un bloc Java afin
de forcer la terminaison du second if.
Exemple. JavaPasAPas/chapitre_3/[Link]
// If ambigu
if (entier1 > 10) {
if (entier2 > 10)
[Link](null,entier1 + " et "+ entier2 + " sont plus grands que 10");
}
else
[Link](null,entier1 + " est inférieur ou égal à 10");
[Link](0);
}
}
77
Nous avons maintenant vu les trois manières d’enchaîner les énoncés :
séquence, boucle et choix. Il est possible de combiner ces trois types
d’énoncés de manière quelconque. Les diagrammes syntaxiques suivants
résument les différents cas d’énoncés vus jusqu’à présent :
énoncé :
déclaration de variable
affectation
appel de méthode
bloc d'énoncés
énoncé while
énoncé for
énoncé if
etc.
bloc d'énoncés :
{ }
énoncé
78
Dans un bloc d’énoncé, il peut y avoir un while, dans le while, un if et dans
le if, un bloc, etc.
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire deux entiers et afficher la division entière.
* Si le diviseur est 0 afficher un message à cet effet.
*/
import [Link];
public class ExerciceIf1 {
if (entier2 == 0)
[Link](null,"La division est impossible");
else
[Link](null,entier1 + "/" + entier2 + "=" + entier1 / entier2);
[Link](0);
}
}
Exercice. Lire deux entiers et afficher le maximum des deux. S’ils sont
égaux, afficher n’importe lequel des deux.
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire trois entiers et afficher le maximum des trois
*/
import [Link];
public class ExerciceIfMax2{
79
[Link](null,"Le maximum des deux entiers est " + entier2);
[Link](0);
}
}
Exercice. Lire deux entiers et afficher le plus grand des deux s’il y en a un
qui est le plus grand, sinon afficher qu’ils sont égaux.
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire trois entiers et afficher le maximum des trois
*/
import [Link];
public class ExerciceIfMax3{
Exercice. Lire 5 entiers et afficher l'entier maximal (le plus grand des cinq
entiers).
80
Solution. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Lire 5 entiers et afficher l'entier maximal
*/
import [Link];
public class ExerciceWhileIf{
public static void main (String args[]) {
String chaine = [Link]("Entrez un nombre");
int plusGrand = [Link] (chaine);
for (int compteur = 1; compteur < 5; compteur=compteur+1){
chaine = [Link]("Entrez un nombre entier");
int entier = [Link] (chaine);
if (entier > plusGrand) {
plusGrand = entier;
}
}
[Link](null,"L'entier le plus grand est " + plusGrand);
[Link](0);
}
}
0<=note<60 E
60<=note<70 D
70<=note<80 C
80<=note<90 B
90<=note<=100 A
81
Types et expressions Java
Dans les premiers chapitres, nous avons rencontré les types int et String et
quelques expressions simples. Ce chapitre approfondit les notions de type et
d’expression Java.
82
54 775 807 (263-
1)
float Nombre réel 45.032F Le F ou f à la fin du littéral
(précision de 45.032f numérique indique que le
32 bits selon 32F nombre doit être de type float
le code 32f
standard IEEE 12.453E5F
754-1985) entre 12.053E-5F 1245300 (La notation En
-3.4*1038 et représente 10n)
3.4*1038 (7 0.0001205
chiffres
significatifs)
double Nombre réel 45.032 Par défaut, un littéral
(précision de 45.032D numérique avec partie décimale
64 bits selon 45.032d est de type double
le code 32D Le D ou d à la fin indique que
standard IEEE 32d le nombre doit être de type
754-1985) entre 12.453E5 double
-1.7*10308 et 12.053E-5
1.7*10308 (15 124. 1245300 (La notation En
chiffres représente 10n)
significatifs) 0.0001205
Figure 15. Types primitifs de Java.
Littéral (litteral)
Une valeur particulière d’un type primitif exprimée par une chaîne de
caractères dans le code source d’un programme est appelée un littéral. Le
tableau précédent montre des exemples de littéraux pour chacun des types
primitifs.
Les types float et double sont des nombres réels avec partie fractionnaire. En
Java, on ne peut pas représenter les valeurs réelles (comme π). On utilise
plutôt des nombres à virgule flottante. Ainsi donc, on peut utiliser des «
double » pour consacrer 64 bits afin de représenter des nombres. On utilise
alors la norme « binary64 » qui accorde 53 bits à la mantisse d’une
représentation binaire. En d’autres mots, on peut pratiquement représenter
n’importe quel nombre de la forme m 2p tant que m n’excède pas 253. En
particulier, le type « double » en Java peut représenter tous les entiers (positifs
et négatifs) qui n’excèdent pas une magnitude de 253. Quand un nombre ne
peut pas être pas être représenté, Java va trouver le nombre à virgule flottante
le plus proche. Si nous arrivons exactement entre deux nombres à virgule
flottante, comme c’est le cas avec le nombre 9000000000000002.5, Java va
choisir le nombre le plus proche qui a une mantisse paire (ici
9000000000000002). Tous les nombres décimaux à 6 chiffres significatifs
peuvent être représentés avec le type float. Par contre, certains nombres
comprenant 6 chiffres significatifs correspondent à plus d’un nombre de
type float. On peut distinguer deux nombres de type float en utilisant 9 chiffres
significatifs. Tous les nombres décimaux à 15 chiffres significatifs peuvent
être représentés avec le type double; on peut distinguer deux nombres de type
double en utilisant 17 chiffres significatifs. Dans la pratique, nous utilisons
donc souvent soit 9 chiffres significatifs, soit 17 chiffres significatifs afin de
stocker en format décimal les nombres. Par exemple, si on sait que le résultat
sera stocké sous la forme d’un double, il est inutile de saisir le nombre π avec
plus de précision que 3.1415926535897932. Les types float et double peuvent
aussi avoir pour valeur –infini, +infini ainsi qu’une valeur spéciale NaN (not-
a-number) que nous pouvons générer en divisant zéro par zéro (par
exemple).
Exemple. JavaPasAPas/chapitre_3/[Link]
84
false
NaN
False
La Figure 15 montre les conventions Java pour exprimer les littéraux des
types numériques. Java permet de former des expressions numériques
complexes de manière analogue aux expressions mathématiques. Le tableau
suivant montre les opérations numériques binaires de Java.
Opération binaire Signification
+ Addition
Exemples : 4 + 5 donne 9, 4.2 + 16.3 donne 20.5
- Soustraction
Exemples : 5 - 2 donne 3, 20.5 – 16.3 donne 4.2
* Multiplication
Exemple : 3 * 4 donne 12
/ Division
Exemples : 16 / 5 donne 3, 16.0 / 5.0 donne 3.2
% Reste après division entière
Exemples : 16 % 4 donne 0, 16 % 5 donne 1
La division par zéro (avec / ou %) dans le cas des entiers génère une erreur
et peut terminer un programme. La division par zéro dans le cas des nombres
85
à virgule flottante est permise, mais elle génère une valeur infinie ou la valeur
NaN.
Dans le cas d’une operation arithmétique dont le résultat ne peut pas être
représenter par le type choisi, un résultat incorrect peut être obtenu. Il est de
votre responsabilité de vérifier que le résultat du calcul peut être représenté.
est équivalente à
(((3 + (2 * 6)) - 3) - (2*4))
dont le résultat est 4. L’évaluation procède donc selon les étapes suivantes :
(((3 + (2 * 6)) - 3) - (2*4))
(((3 + 12) - 3) - (2*4))
86
(((3 + 12) - 3) - 8)
((15 - 3) - 8)
(12 - 8)
4
Les parenthèses permettent de modifier cet ordre d’évaluation au besoin.
Conseil
Lorsque des opérandes de types différents sont combinés, Java effectue des
conversions de type automatiques en convertissant à un type unique tous les
opérandes de l’expression.
87
Lorsqu’une conversion non valide est voulue par le programmeur, il est
possible de forcer une conversion par une opération de conversion (cast)
dont la syntaxe est :
(nomDuType)valeur
La valeur sera alors convertie dans le type entre parenthèses. La conversion
peut entraîner une perte de précision comme l’illustre l’exemple suivant.
Sans conversion explicite, une erreur serait levée car cette promotion
automatique n’est pas valide en Java. La conversion explicite est
potentiellement dangereuse. En tant que programmeur, vous avez la
responsabilité de vérifier que le nouveau type peut représenter la valeur.
Considérons l’exemple suivant.
Exemple. JavaPasAPas/chapitre_3/[Link]
/**
* [Link]
* Petit exemple illustrant l'énoncé if.
*/
import [Link];
public class ExempleLogique{
public static void main (String args[]) {
String unString = [Link]("Entrez un premier
nombre entier");
int unInt = [Link](unString);
if (unInt > 10 & unInt < 20) { [Link](null,unInt
+ " est entre 10 et 20"); }
if (unInt == 100 | unInt == 200) {
[Link](null,unInt + " est 100 ou 200"); }
if (!(unInt > 30)) { [Link](null,unInt + " n'est pas
plus grand que 30"); }
[Link](0);
}
}
La condition
(unInt > 10 & unInt < 20)
est évaluée à vrai si unInt est à la fois plus grand que 10 et plus petit que 20.
Lorsque l’on utilise un &, il faut que les deux parties de la condition soient
vraies pour que le résultat soit vrai.
La condition
89
if (unInt == 100 | unInt == 200)
est évaluée à vrai si unInt est égal à 10 ou unInt est égal à 20. Une seule des
deux parties doit être vraie pour que le résultat soit vrai. Le ou n’est pas
exclusif, c’est-à-dire que si les deux parties sont vraies, le résultat est vrai.
La condition
!(unInt > 30)
est évaluée à vrai si inInt > 30 est faux, donc si inInt <= 30.
Java inclut aussi le && et le ||. Le && est une variante du & qui court-
circuite l’évaluation lorsque possible. Le & évalue toujours les deux parties
de la condition alors que le && n’évalue pas la deuxième partie si la première
est fausse. On dit que l’évaluation est court-circuitée. En effet, si la première
partie est fausse, le résultat de la condition sera nécessairement faux. Il n’est
donc pas nécessaire d’évaluer la deuxième partie.
Exemple. L’expression
donne false. Comme 2 > 3 donne false, il n’est pas nécessaire d’évaluer la
deuxième partie 2 < 10. Le && évite d’évaluer la seconde partie, 2 < 10.
Dans la plupart des circonstances, le résultat de & et && est identique.
Cependant, si l’évaluation de la partie droite peut avoir un effet autre que
l’évaluation de la condition elle-même, le résultat ne sera pas toujours le
même.
Exemple. JavaPasAPas/chapitre_3/[Link]
90
/**
* [Link]
* Petit exemple illustrant l'énoncé if.
*/
import [Link];
public class ExempleEtCourtcircuite{
public static void main (String args[]) {
String unString = [Link]("Entrez un premier nombre entier");
int unInt = [Link](unString);
[Link]
On peut aussi voir le type char comme une valeur entière entre 0 et 65536.
Le type char occupe autant d’espace en mémoire que le type short, mais le
type short est utilisé pour représenter les valeurs entières entre -32768 et
327678.
91
La Figure 15 montre des exemples de littéraux du type char. Un caractère
imprimable est représenté entre apostrophes. On peut aussi employer le
code Unicode du caractère selon le format :
'\uyyyy'
où yyyy est une suite de quatre chiffres hexadécimaux qui correspond au code
Unicode hexadécimal du caractère. Le \u est une séquence dite d’échappement
qui altère l’interprétation normale par le compilateur Java. La séquence
indique au compilateur que ce qui suit est le code Unicode du caractère et
non pas le caractère lui-même. Le tableau suivant montre d’autres séquences
d’échappement prévues en Java.
92
ressortir certains aspects fondamentaux qui distinguent les objets (de classes)
et les valeurs (de types primitifs).
• Constructeur d’objet
Exemple.
JavaPasAPas/chapitre_3/[Link]
"abcdef"
93
L’appel new String("abcdef") dans la ligne suivante
String string1 = new String("abcdef");
crée un objet de la classe String dont le contenu est "abcdef". L’objet
contient la chaîne de caractères "abcdef". Il contient la chaîne mais n’est pas la
chaîne ! Lorsqu’un objet est créé, un identifiant d’objet (OID) lui est assigné
automatiquement. Dans notre exemple, l’objet crée a l’OID = 4000. Cette
valeur n’est donnée qu’à titre d’exemple et n’a pas d’importance en soi. On
ne doit pas se préoccuper de la manière dont les OID sont générés. En fait,
l’OID n’est pas visible dans le programme Java et n’est pas manipulable
directement. Un OID est en quelque sorte une adresse pour retrouver un
objet. Il est analogue à un numéro d’assurance sociale pour un citoyen. Le
numéro en soi n’a pas d’importance. Ce qui compte, c’est qu’il permet
d’identifier un citoyen sans ambiguïté. L’adresse en mémoire centrale est une
manière de réaliser un OID, mais il y a aussi d’autres implémentations
possibles.
Dans l’énoncé
String string1 = new String("abcdef");
l’objet de la classe String créé dans la partie droite est affecté à la variable
string1 de la partie gauche. Le type de string1 doit être le même que celui de
l’objet créé23. C’est pourquoi, le type de string1 est String. Il est fréquent de
rencontrer en Java ce genre d’énoncé où une variable de type ClasseX est
déclarée et on lui affecte un objet de type ClasseX créé par new ClasseX().
Lorsqu’un objet est affecté à une variable, c’est l’OID de l’objet qui est placé
dans la variable. On dit alors que la variable contient une référence à l’objet.
Souvent les références sont représentées graphiquement par des flèches tel
qu’illustré dans la figure suivante car les valeurs exactes des OID sont sans
importance. Ce qui compte, c’est que la variable fasse référence au bon objet.
23 Nous verrons plus loin que ce n’est pas nécessairement le même type dans certaines
circonstances
94
objet de la classe String (OID=4000)
string1
"abcdef"
string2
"abcdef"
Une valeur de type String ne peut pas être modifiée. Une fois que la valeur
« abcdef » a été assignée à la variable, on ne peut plus changer la chaîne. C’est
un choix spécifique à cette classe puisqu’il aurait été possible pour les
créateurs du Java de faire en sorte que la classe String puisse modifier sont
contenu.
La ligne
String string2 = string1;
affecte le contenu de string1 à string2. Ceci ne copie pas l’objet mais
plutôt l’OID de l’objet. En conséquence, les deux variables font maintenant
référence au même objet !
La ligne
String string3 = new String("abcdef");
crée un autre objet de la classe String, dont l’OID = 4050. Cet autre objet
contient aussi "abcdef" mais c’est un objet différent du premier !
95
Par opposition aux objets, il n’y a pas de distinction entre une valeur et son
contenant pour les types primitifs. Comment fait-on la différence entre la
référence à l’objet et le contenu de l’objet dans un programme ? La réponse
à cette question est illustrée par le reste du code du programme.
Dans le cas d’objets, le « == » Java compare les références aux objets et non
pas le contenu des objets. Ainsi le test string1 == string2 dans
[Link](string1 == string2); //true
produit la valeur true car les deux variables font référence au même objet mais
string1 == string3 dans
[Link](string1 == string3); //false
est false car les deux variables string1 et string3 font référence à des objets
différents ! Pour comparer le contenu des objets String, on peut utiliser la
méthode equals() de la classe String. Ainsi le test [Link](string3) dans
[Link]([Link](string3)); //true
produit la valeur true parce que le contenu des deux objets est le même.
Attention !
96
[Link]
Cette documentation est sous forme HTML, et elle peut être consultée à
partir d’un fureteur Web. Notez que même si les versions de Java se succède
rapidement, il est souvent possible de tout faire avec les classes de la version
8. Il y a d’ailleurs des bénéfices à ne pas trop rapidement adopter des
fonctions et des classes qui ne sont disponibles qu’avec des versions récentes
du Java.
97
Exemple. Le programme suivant illustre quelques méthodes de la classe
String.
Résultat affiché :
String string1 = new String("abcdef")
String string2 = new String("cd")
La longueur de string1 est :6
Le caractère en position 2 de string1 est :c
La sous-chaine en position 2 de string1 est :cdef
La sous-chaine qui débute en position 2 et fini en 4 est :cde
La première occurrence de string2 dans string1 est à la position
:2
La concaténation de string2 à string1 donne :abcdefcd
98
L’appel [Link](2) retourne le caractère (ou plutôt le mot de 16 bits) en
position 2 de string1, ce qui correspond au troisième caractère de "abcdef",
c’est-à-dire le caractère « c » car les positions des caractères sont numérotées
à partir de la position 0 !
position 0 1 2 3 4 5
caractère a b c d e f
A noter que les deux appels utilisent le même nom de méthode mais avec
des paramètres différents ! En réalité, ces deux appels invoquent deux
méthodes différentes. D’ailleurs, dans l’extrait suivant de la documentation,
la méthode substring() apparaît deux fois.
99
Returns a string that is a substring of
this string.
Ces deux méthodes de même nom désignent en réalité deux méthodes
différentes. Le compilateur peut distinguer les méthodes de même nom par
le fait que la nature des paramètres est différente. Dans
substring(int beginIndex), il n’y a qu’un paramètre de type int. Cette
méthode retourne un objet String dont la chaîne est la sous-chaîne qui
débute à la position beginIndex et termine au dernier caractère du String.
Dans substring(int beginIndex, int endIndex), il y a deux
paramètres et la méthode retourne un objet String qui débute à la position
beginIndex et se termine à la position endIndex. Ce sont bien deux
méthodes différentes mais apparentées.
• Littéral String
100
permet d’utiliser un littéral String. Un littéral String est une séquence de
caractères entre guillemets.
Exemple. JavaPasAPas/chapitre_3/[Link]
101
public class ExemplesString{
La condition dans
[Link](string3 == "abcdef"); // true
retourne true car string3 fait référence au même objet que "abcdef".
La condition dans
[Link]("abc"+"def" == "abcdef"); //true
retourne aussi true car la concaténation "abc"+"def" est calculée au moment
de la compilation, ce qui produit le littéral "abcdef".
La condition dans
[Link](string1 + string2 == "abcdef"); //false
retourne false car la concaténation string1 + string2 ne peut être calculée à la
compilation. Donc, l’objet créé ne sera pas le même que l’objet
correspondant au littéral "abcdef".
La condition
102
[Link](string4 == "abcdef"); //false
retourne false car le constructeur String() produit toujours un objet distinct et
donc différent de l’objet correspondant au littéral.
• le littéral null
Exemple. JavaPasAPas/chapitre_3/[Link]
103
[Link]([Link]("")); //true
[Link](string2 == null); //false
""
Enfin, notons que l’accès à une variable non initialisée provoque une erreur
de compilation. Java vous protège contre l’accès aux variables non initialisée
parce que de tels accès sont souvent la source de problèmes et de bogues.
104
Exemple. JavaPasAPas/chapitre_3/[Link]
Exemple. JavaPasAPas/chapitre_3/[Link]
Exemple. JavaPasAPas/chapitre_3/[Link]
105
L’exemple suivant montre quelques exemples de fonctions mathématiques
usuelles.
public class ExemplesMath {
Résultat affiché :
[Link](1.0)=0.0
[Link](1.0)=2.718281828459045
[Link](0)=1.0
[Link](0)=0.0
[Link](4)=2.0
106
>>> Décalage à droite sans signe (niveau bit)
< Plus petit que
> Plus grand que
<= Plus petit ou égal à
>= Plus grand ou égal à
instanceof Est une instance de
== Est égal à
!= Est différent de
& Et (niveau bit / logique)
^ Ou exclusif (niveau bit / logique)
| Ou inclusif (niveau bit / logique)
&& Et logique
|| Ou logique
?: Expression conditionnelle
= Affectation
+= Auto-addition
-= Auto-soustraction
/= Auto-division
%= Auto-reste
^= Auto-ou-exclusif (niveau bit)
|= Auto-ou (niveau bit)
<<= Auto-décalage à gauche (niveau bit)
>>= Auto-décalage à droite (niveau bit)
>>>= Auto-décalage à droite sans signe (niveau bit)
107