Infoforall

Java 02 : GESTION DES CHOIX LOGIQUES

Vous allez voir aujourd’hui l’une des bases de l’algorithme : Les instructions conditionnelles (à faire si la condition est vraie) et les instructions itératives (à faire un certain nombre de fois).

Le Si

Une fois ces outils accessibles, les programmes peuvent devenir rapidement beaucoup plus complexes et intéressant en terme de gestion automatisée des tâches.

1 - Le type boolean (booléen en français)

En introduction, voilà le codage de différents test de conditions logiques entre deux variables :

Symbole du testSignificationExemple
==Est égal àA == B
!=Est différent deA != B
>Est supérieur àA > B
>=Est supérieur ou égal àA >= B
<Est inférieur àA < B
<=Est inférieur ou égal àA <= B

Chacun de ces tests ne peut donner normalement que deux résultats : true pour vrai ou false pour faux. Attention, contrairement à Python, il n'y a pas de majuscules ici. Pour ceux qui travaillent sur les deux langages, il va falloir y faire attention.

Ainsi, on peut déclarer un boolean de cette façon :

boolean test = a>b; va créer un boolean qui vaut true si a>b et false sinon.

2 - L’instruction conditionnelle SI – SINON

Commençons par voir comment choisir une action en fonction d’une entrée. C’est une action logique qu’on peut traduire en français par :

En françaisCodage JAVA
SI la première condition est vraie alorsif (condition_1) {
    Fait les actions 1    Instructions_1;
    Instructions_1;
}
SINON SI la deuxième condition est vraie alorselse if (condition_2) {
    Fait les actions 2    Instructions_2;
    Instructions_2;
}
Sinonelse {
    Fait les actions 3    Instructions_3;
    Instructions_2;
}
Fin du testAucune déclaration en JAVA

Les instructions à effectuer dans chacun des cas sont à inclure entre des accolades { et }. Attention, le passage à la ligne et la position des accolades n'a pas d'incidence sur l'interprétation : il faut juste que le code à exécuter soit bien entre les deux accolades.

01° Application : Faisons un petit programme qui nous permet de lire un nombre rentré au clavier puis de dire s’il est positif, négatif ou nul.

N'oublier pas de créer un nouveau projet et une nouvelle classe. Où bien garder votre ancien projet. N'oubliez pas d'insérer import java.util.Scanner; juste avant la déclaration de votre classe.

Je ne donne ici que la méthode main pour gagner un peu de place :

public static void main(String[] args) {

    Scanner sc = new Scanner(System.in); // On crée un objet sc de classe Scanner

    System.out.println("Veuillez saisir un nombre entier :");

    int nombre = sc.nextInt();

    System.out.println("Vous avez saisi : " + nombre);

    if (nombre < 0) {

        System.out.println("Ce nombre est négatif !");

    }

    else {// sinon, c'est que le nombre est négatif ou nul non ?

        if(nombre == 0)

            System.out.println("Ce nombre est nul !");// Une seule instruction

        else

            System.out.println("Ce nombre est positif !");// Une seule instruction

    }

}

02° Modifier ensuite le code pour qu’il puisse signaler en plus si le nombre est supérieur à 255 (toujours strictement), inférieur (strictement) à 125 ou égal à 125 (attention, l’égalité se teste avec ==, pas = qui caractérise une affectation).

Remarque 2 : si vous avez été vigilant, vous auriez dû voir que je n'ai pas utilisé les accolades dans le if inclus dans le else principal (celui avec le commentaire). Pourquoi cela n'a-t-il pas déclencher d'erreur ? C'est simple : les accolades ne sont obligatoires que si on exécute plusieurs instructions. Or, il n'y a qu'un print dans chaque bloc d'instructions. Retenons : lorsqu'un bloc d'instruction ne contient qu'une seule instruction, on peut se passer des { et } sur ce bloc.

03° Pour l'instant les tests ne se font qu'avec un if puis un else dans lequel on refait des tests if. Simplifiez le programme en utilisant uniquement des conditions if, else if, else if et else non imbriquées les unes dans les autres. Pensez à m’appeler si vous êtes bloqué.

Exemple de structure en if - else if - else (sans accolades { } car chaque bloc ne comporte qu'une instruction unique).

public static void main(String[] args) {

    Scanner sc = new Scanner(System.in); // On crée un objet sc de classe Scanner

    System.out.println("Veuillez saisir votre note :");

    int nombre = sc.nextInt();

    System.out.println("Vous avez saisi : " + nombre);

    if (nombre > 20)

        System.out.println("C'est étrange !");

    else if (nombre == 20)

        System.out.println("Sans faute !");

    else if (nombre > 15)

        System.out.println("Bien !");

    else if (nombre > 10)

        System.out.println("Correct !");

    else if (nombre > 8)

        System.out.println("Trop juste !");

    else if (nombre > 4)

        System.out.println("Mais que faites vous ?");

    else if (nombre >= 0)

        System.out.println("Nul nul nul !");

    else

        System.out.println("Soit votre note est négative, soit quelqu'un a fait une erreur !");

    /* C'est ici que commence la lecture du code lorsqu'on sort des blocs de if */

}

Le truc a bien comprendre est qu'on sort de la structure par le bas dès qu'on a fait le bloc d'instruction d'une condition valide. Ainsi, si la condition >15 est true, l'ordinateur n'ira pas ensuite voir si la condition suivante >10 est validée ou non : il sort tout simplement de l'ensemble if et continue à lire le code suivant.

Il reste à tester des conditions doubles. Une condition renvoie un booléen qui est true ou false. On peut tester plusieurs conditions à la fois à l’aide des codages suivants :

TEST LOGIQUEEn FrançaisEn JAVA
andcode ETA & B : Renvoie true si les deux conditions A et B sont true.
andcode ETA && B : Comme ci-dessus mais on ne teste pas B si A est faux.
orcode OU inclusifA | B : Renvoie true si l'une au moins des deux conditions est true.
orcode OU inclusifA || B : Comme ci-dessus mais on ne teste B que si A est false.
orcode OU exclusifA ^ B : Ne renvoie true que si une et uniquement une des conditions est true
notcode NON!A : Renvoie true si A == false et renvoie false si A == true.

Le signe du OU : | s'obtient par un ALT GR + 6.

Les opérateurs de court-circuit : Les tests && et || permettent de gagner du temps de calcul : on évalue la condition suivante que si cela est nécessaire. Dans le cas du ET, il ne sert à rien d'évaluer B si A est false. Dans le cas du OU, il ne sert à rien d'évaluer B si A est déjà true.

Cela permet également d'éviter des erreurs : imaginons que A est la condition "x est une image" et que la condition B est "largeur supérieure à 600 pixels". Avec un & simple, on va tenter de connaitre la largeur de x même si x n'est pas une image ! Le programme risque de renvoyer une erreur. Avec &&, il détectera que A est false si x n'est pas une image et ne tentera pas d'évaluer B (la largeur d'une supposée image).

Application pratique : nous voudrions tester qu'une entrée clavier est bien un integer et dans ce cas, dire si l'entier est supérieur à 10.

Le test permettant de savoir si une entrée clavier sc contient un entier est simple : sc.hasNextInt() renvoie true si on peut lire un entier dans le flux sc.

Voilà un code qui vous donne deux façons de faire ceci avec les deux types de ET. Dans les deux cas, on teste la présence d'un entier et on teste si la valeur est supérieure à 10.

04° Tester le code ci-dessous qui propose deux fois de lire un entier. Lancer le code une première fois en donnant deux fois un entier. Recommencer ensuite en tapant par exemple "Bonjour" pour l'un ou pour l'autre.

public static void main(String[] args) {

    Scanner sc = new Scanner(System.in); // On crée un objet sc de classe Scanner

    System.out.println("Veuillez saisir votre note :");

    // Premier test avec un simple ET

    int nombre = 0;

    if (sc.hasNextInt() & (sc.nextInt() > 10)) {

        System.out.println("Votre nombre est supérieur à 10");

    }

    // Second test avec un &&

    nombre = 0;

    System.out.println("Veuillez saisir votre note :");

    if (sc.hasNextInt() && (sc.nextInt() > 10)) {

        System.out.println("Votre nombre est supérieur à 10");

    }

}

Pourquoi le seconde solution ne plante pas avec "Bonjour" ? Simplement à cause du double && : puisque la première condition est fausse (l'entrée n'est pas un entier), il ne va pas vérifier la deuxième qui plante puisqu'on ne parviens pas à comparer l'entrée texte à un nombre.

05° Imaginons que nous voulions tester si un élève peut être accepté dans une filière contenant beaucoup de maths et de physiques. On vous demande de créer un programme qui nous demande de rentrer la moyenne annuelle de maths puis la note annuelle de physique. Si les deux notes sont supérieurs à 14, on veut indiquer qu’il est pris. Si les deux notes sont inférieures à 10, il est refusé. Sinon, il est sur liste d’attente. A vous de jouer.

Après la condition ET, voilà la condition OU codé par | ou || ou ^. Compliquons les choses, nous voulons :

06° Codez le cas précédent.

3 - Boucle WHILE ou TANT QUE

Passons maintenant aux boucles : on peut demander au programme de faire plusieurs fois la même chose, soit un nombre de fois précis, soit tant qu’une condition n’est pas respectée.

La première boucle est le WHILE ou tant que en français : Tant que la condition est vraie, le programme va lancer les instructions à l’intérieur de la structure. Il est donc possible que cette instruction ne se fasse jamais si la condition n’est pas vérifiée initialement.

while (Condition) {

    //Instruction1 à répéter

    //Instruction2 à répéter

}

On retrouve encore un bloc commençant par { et finissant par }.

Exemple : voilà un programme qui demande une note jusqu’à ce qu’elle soit comprise entre 0 et 20 :

public static void main(String[] args) {

    int nombre1 = -1;

    Scanner sc = new Scanner(System.in);

    while (nombre1 > 20 || nombre1 < 0) {

        System.out.println("Veuillez saisir une note :");

        nombre1 = sc.nextInt();

    }

    System.out.println("La note prise en compte est " +nombre1 +"/20");

}

07° Je vous propose un code pas si facile que ça à mettre en oeuvre : demander une note jusqu'à ce que le résultat soit compris entre 0 et 20 (c'est le code ci-dessus) ou qu'il soit égale à "ABS" pour absent. En cas de mauvaise saisie, on recommence à demander une nouvelle note.

Vous allez devoir utiliser la méthode hasNextInt() afin de tester si la réponse est à priori une note entière ou du texte.

Vous pouvez tester qu'on a une ligne en attente avec hasNextLine().

Vous allez certainement devoir gérer votre while à l'aide d'une variable booléenne qu'on pourrait nommer test.

Cette variable test devra être devenir fausse si la réponse est un nombre compris entre 0 et 20 ou si la réponse vaut "ABS".

Attention : on ne peut pas tester l'égalité de deux Strings avec ==. Cette instruction vérifie que les deux objets Strings possèdent la même adresse, pas qu'elles ont le même contenu. Pour vérifier que les chaînes sont remplies de la même manière, il faut utiliser la méthode equals et taper chaine1.equals(chaine2) où chaine1 et chaine2 sont les chaînes à tester.

Vous avez l'essentiel. Reste à le mettre en forme...

Oui oui, c'est difficile.

Une solution possible avec les informations proposées :

public static void main(String[] args) {

    // Initialisation

    int nombre1 = -1;

    String texte ="";

    boolean test = true;

    // Creation de sc

    Scanner sc = new Scanner(System.in);

    // Tant que le test est vrai

    while (test) {

        System.out.println("Veuillez saisir une note :");

        // Si on détecte qu'on a un nombre dans le flux clavier

        if (sc.hasNextInt()) {

            nombre1 = sc.nextInt();

            if (nombre1 >=0 && nombre1 <=20) {

                test = false;

                // La ligne suivante sert à transformer la donnée chiffrée en texte

                texte = ""+nombre1;

            }

        }

        // Si on détecte une ligne dans le flux

        else if (sc.hasNextLine()) {

            texte = sc.nextLine();

            // Si le contenu de texte est "ABS", pas de == mais bien equal()

            if (texte.equals("ABS"))

                    test = false;

        }

    }

    System.out.println("La note prise en compte est " +texte);

    // Ca, c'est nouveau pour vous : on coupe le flux, on n'en a plus besoin

    sc.close();

}

Tester les caractères est plus simple. Ce ne sont pas des objets (comme les Strings). On peut utiliser un simple == pour comparer deux caractères.

Le problème en Java est de récupérer un caractère depuis le clavier. En effet, la méthode nextLine() appliquée à un objet de classe Scanner crée un objet de classe String : un assemblage de caractères et non pas UN caractère. Et il n'existe pas de méthode nextChar(). Ainsi, même si vous répondez O, la méthode va créer un String qui contient "O" et pas un caractère qui contient 'O'.

Le string monTexte = "Bonjour, je suis ici." peut se voir comme le tableau suivant :

Case 00 : contient : B
Case 01 : contient : o
Case 02 : contient : n
Case 03 : contient : j
Case 04 : contient : o
Case 05 : contient : u
Case 06 : contient : r
Case 07 : contient : ,
Case 08 : contient :  
Case 09 : contient : j
Case 10 : contient : e
Case 11 : contient :  
Case 12 : contient : s
Case 13 : contient : u
Case 14 : contient : i
Case 15 : contient : s
Case 16 : contient :  
Case 17 : contient : i
Case 18 : contient : c
Case 19 : contient : i
Case 20 : contient : .

Attention : comme d’habitude en informatique, le premier élément est l’élément 0.

Alors comment accéder aux contenus des cases ? Avec une méthode qui s'applique aux objets de classe String. Il s'agit de la méthode charAt(x) où x est le numéro de la case voulue.

Pour récupérer le premier caractère tapé au clavier, il suffit donc d'utiliser reponse = sc.nextLine().charAt(0); ou reponse = reponseTotale.charAt(0); si reponseTotale contient sc.nextLine().

08° Réaliser un programme qui augmente un entier de 1 puis demande si on veut continuer. Si on répond 'N', on sort de la boucle. Sinon, on recommence en rajoutant 1.

Une réponse possible est dans le fichier txt fourni si vraiment vous n'arrivez pas à vous dépatouiller.

La suite prochainement.

4 - Boucle DO WHILE ou FAIRE TANT QUE

Autre boucle : la boucle DO..WHILE. C’est un peu pareil à une grosse nuance près : on rentre au moins une fois dans la boucle puisqu’on teste la condition de sortie à la fin.

do {

    //Instruction1 à répéter

    //Instruction2 à répéter

}

while (condition_du_tant_que) ;

Attention à ne pas oublier le ; final, sinon ça ne marchera pas.

09° Réaliser un programme utilisant un do...while qui demande un nombre et qui additionne les nombres rentrés tant qu’on rentre des nombres inférieurs à 20. Une fois sorti de la boucle do...while, il donne la somme des nombres.

5 - Boucle FOR

La dernière boucle usuelle : la boucle FOR. Celle ci permet d’effectuer une tâche un certain nombre de fois. En gros, on teste sur le nombre de fois que la boucle a été effectuée.

for(int i = 1; i <= 10; i=i+1) {

    System.out.println("Voici la ligne "+i);

}

Ce programme va réaliser le bloc d'instructions 10 fois pour i valant 1,2,3,4,5,6,7,8,9,10 (on incrémente i de 1 à chaque boucle à cause de l’instruction i++.

10° Créer un programme qui demande à l'aide d'une boucle trois fois une note, les additionne et donne la moyenne obtenue une fois sorti de la boucle FOR. Pensez à utiliser l'opérateur de cast (float) pour garantir que la moyenne ne soit pas arrondie.

Plutôt que de taper i = i+1, on peut utiliser l'opérateur d'incrémentation ++ : on pourrait noter i++.

11° Modifier le programme précédent en utilisant cet opérateur.

Pour décrémenter, on utilise alors i-- ou i = i-1 :

for(int i = 10; i >= 0; i--) {

    System.out.println("Il reste " + i + " ligne(s) à écrire");

}

12° Réaliser un programme qui affiche les carrés de 20 à 10.

13° Idem mais uniquement pour 20, 18, 16 ... jusqu'à 10. Il faudra donc modifier i de 2 à chaque fois à l'aide de i = i-2.

6 - Boucle FOR et chaîne de caractères

Comme un String est composé d'un ensemble de caractères, on va pouvoir utilser une boucle FOR pour parcourir le String et lire les caractères un à un.

String chaine = "J'aime les kiwis.";

for (int i = 0 ; i < chaine.length() ; i++) { // chaine doit être un objet de classe String

    System.out.println("Le String chaine contient : " + chaine.charAt(i));

}

La méthode length() renvoie la longueur du String, à savoir le nombre de charactères utilisés. Comme la première case est la case 0, on peut faire le test avec un simple inférieur < : si la chaine contient 15 caractères, son dernier caractère est le caractère 14 puisque le premier est le caractère 0.

14° Créer un programme qui énumère les caractères d'une chaîne de caractères que l'utilisateur rentre au clavier.

15° Idem mais on veut donner la valeur décimale Unicode du caractère plutôt que le caractère lui même.

7 - Les instructions break et continue

Parfois, on désire sortir d'une boucle avant sa conclusion normale. On pourra par exemple vouloir lire les caractères d'une chaîne de caractères (un String) mais stopper la procédure dès qu'on rencontre un w.

Il suffit alors de placer une instruction break (suvie d'un ; puisque c'est une instruction). Lorsque le programme rencontre cette instruction, il sort de la boucle dans lequel est placé le break.

String chaine = "J'aime les kiwis.";

for (int i = 0 ; i < chaine.length() ; i++) { // chaine doit être un objet de classe String

    if (chaine.charAt(i) == 'w')

        break;

    System.out.println("Le String chaine contient : " + chaine.charAt(i));

}

16° Modifier le code pour bien afficher le caractère et pas son code, puis tester le pour voir quand l'affichage de la chaine s'arrête.

Dernière instruction de sortie : le continue. C'est le même principe que le break mais on ne sort pas de la boucle par le bas mais par le haut : cela veut dire qu'on continue l'itération comme si on était arrivé à la fin du bloc. Ainsi, si on rencontre un continue pour i valant 5, on arrête de suivre le bloc d'instructions et on remonte dans la boucle de for et on recommence le bloc au début pour i valant 6.

17° Modifier le code pour que le programme n'affiche pas les 'e'. Il faudra bien entendu utiliser un continue, même si on peut le faire sans cette instruction.

Il existe une façon plus simple de parcourir une chaine de caractères mais elle ne doit pas rester en String : on doit la transformer en simple suite de caractères. On utilisera pour cela la méthode toCharArray. Attention, après avoir appliqué cette méthode, l'objet obtenu n'est plus un String, on ne peut donc plus utilisé les méthodes propres au String sur cet objet ! Notez bien la présence du : qui fait le lien entre ce qu'on doit lire (c de type char) et l'objet dans lequel on doit lire (monTableau, un tableau de char).

String chaine = "J'aime les kiwis."; // On crée un objet de class String

char[] monTableau = chaine.toCharArray(); // On transforme le String en simple tableau de char

for (char c : monTableau) { // On lit les caractères c dans le tableau de char monTableau

    System.out.println("Le String chaine contient : " + c);

}

Attention : Avec la méthode du for numérique (avec le i), on n'accède pas directement au contenu : i est le numéro de la case. Ici, c est directement le caractère contenu. Nous n'avons pas besoin de rajouter quoi que ce soit pour l'afficher.

18° Utiliser cette façon de faire pour répondre à la question 17. Il faut donc modifier en partie votre code.

8 - La structure SWITCH

Il arrive parfois qu'on veuille faire un choix en fonction de la valeur d'un élément. On peut s'en sortir en utilisant une suite de IF, ELSE IF, ELSE IF ... En Java, il existe une structure plus adaptée à ce type de situation : le SWITCH.

C'est une structure qui permet de choisir le type d'inscructions à suivre en fonction de la valeur précise contenue dans une variable.

Voilà cette structure :

public static void main(String[] args) {

    int nombre = 2;

    String sMois;

    switch(nombre) { // On va travailler avec la variable nombre

        case 1 :

            sMois = "Janvier";

            System.out.println("Nous sommes au mois de Janvier");

            break;

        case 2 :

            sMois = "Février";

            System.out.println("Nous sommes au mois de Février");

            break;

        case 3 :

            sMois = "Mars";

            System.out.println("Nous sommes au mois de Mars");

            break;

        default :

            sMois = "Mois inconnu";

            System.out.println("Le programme ne connait pas encore ce mois.");

            break;

     }

    System.out.println("Le mois choisi est " + sMois);

}

19° Rajoutez quelques mois et vérifiez que le progamme fonctionne.

Mais à quoi servent les break ? A sortir du choix par le bas comme avec le for.

20° Rajouter // devant les breaks pour les placer en commentaires et donc les enlever du code.

On voit donc que sans le break, la structure switch va executer toutes les lignes de code qui sont sous la sélection choisie. Cela peut s'avérer pratique dans certains cas.

Voilà, c'est fini. Il nous reste à voir la notion d'étiquettes pour le break mais nous pourrons voir cela dans un prochain chapitre : il s'agit de la possibilité d'indiquer à un break contenue dans une boucle dans une seconde boucle de savoir où sortir exactement. Par défaut, il sort de la boucle dans laquelle il est déclaré. Si on veut sortir de l'ensemble, il faut utiliser les étiquettes. Souvenez-vous de cela et faites une recherche si vous en avez besoin avant de rencontrer la notion ici.