Les structures de contrôle

Comme la plupart des langages impératifs, Java propose un ensemble de structures de contrôle.

if-else

L’expression if permet d’exécuter un bloc d’instructions uniquement si l’expression booléenne est évaluée à vrai :

if (i % 2 == 0) {
  // instructions à exécuter si i est pair
}

L’expression if peut être optionnellement suivie d’une expression else pour les cas où l’expression est évaluée à faux :

if (i % 2 == 0) {
  // instructions à exécuter si i est pair
} else {
  // instructions à exécuter si i est impair
}

L’expression else peut être suivie d’une nouvelle instruction if afin de réaliser des choix multiples :

if (i % 2 == 0) {
  // instructions à exécuter si i pair
} else if (i > 10) {
  // instructions à exécuter si i est impair et supérieur à 10
} else {
  // instructions à exécuter dans tous les autres cas
}

Note

Si le bloc d’instruction d’un if ne comporte qu’une seule instruction, alors les accolades peuvent être omises :

if (i % 2 == 0)
  i++;

Cependant, beaucoup de développeurs Java préfèrent utiliser systématiquement les accolades.

return

return est un mot clé permettant d’arrêter immédiatement le traitement d’une méthode et de retourner la valeur de l’expression spécifiée après ce mot-clé. Si la méthode ne retourne pas de valeur (void), alors on utilise le mot-clé return seul. L’exécution d’un return entraîne la fin d’une structure de contrôle.

if (i % 2 == 0) {
  return 0;
}

Écrire des instructions immédiatement après une instruction return n’a pas de sens puisqu’elles ne seront jamais exécutées. Le compilateur Java le signalera par une erreur unreachable code.

if (i % 2 == 0) {
  return 0;
  i++; // Erreur de compilation : unreachable code
}

while

L’expression while permet de définir un bloc d’instructions à répéter tant que l’expression booléenne est évaluée à vrai.

while (i % 2 == 0) {
  // instructions à exécuter tant que i est pair
}

L’expression booléenne est évaluée au départ et après chaque exécution du bloc d’instructions.

Note

Si le bloc d’instruction d’un while ne comporte qu’une seule instruction, alors les accolades peuvent être omises :

while (i % 2 == 0)
  // instruction à exécuter tant que i est pair

Cependant, beaucoup de développeurs Java préfèrent utiliser systématiquement les accolades.

do-while

Il existe une variante de la structure précédente, nommée do-while :

do {
  // instructions à exécuter
} while (i % 2 == 0);

Dans ce cas, le bloc d’instruction est exécuté une fois puis l’expression booléenne est évaluée. Cela signifie qu’avec un do-while, le bloc d’instruction est exécuté au moins une fois.

for

Une expression for permet de réaliser une itération. Elle commence par réaliser une initialisation puis évalue une expression booléenne. Tant que cette expression booléenne est évaluée à vrai, le bloc d’instructions est exécuté et un incrément est appelé.

for (initialisation; expression booléenne; incrément) {
  bloc d'instructions
}
for (int i = 0; i < 10; ++i) {
  // instructions
}

Note

il n’est pas possible d’omettre l’initialisation, l’expression booléenne ou l’incrément dans la déclaration d’une expression for. Par contre, il est possible de les laisser vide.

int i = 0;
for (; i < 10; ++i) {
  // instructions
}

Il est ainsi possible d’écrire une expression for sans condition de sortie, la fameuse boucle infinie :

for (;;) {
  // instructions à exécuter à l'infini
}

Note

Si le bloc d’instruction d’un for ne comporte qu’une seule instruction, alors les accolades peuvent être omises :

for (int i = 0; i < 10; ++i)
  // instruction à exécuter

Cependant, beaucoup de développeurs Java préfèrent utiliser systématiquement les accolades.

for amélioré

Il existe une forme améliorée de l’expression for (souvent appelée for-each) qui permet d’exprimer plus succinctement un parcours d’une collection d’éléments.

for (int i : maCollection) {
  // instructions à exécuter
}

Pour que cette expression compile, il faut que la variable désignant la collection à droite de : implémente le type Iterable ou qu’il s’agisse d’un tableau. Il faut également que la variable à gauche de : soit compatible pour l’assignation d’un élément de la collection.

short arrayOfShort[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for (int k : arrayOfShort) {
  System.out.println(k);
}

break-continue

Pour les expressions while, do-while, for permettant de réaliser des itérations, il est possible de contrôler le comportement à l’intérieur de la boucle grâce aux mots-clés break et continue.

break quitte la boucle sans exécuter le reste des instructions.

int k = 10;
for (int i = 1 ; i < 10; ++i) {
  k *= i
  if (k > 200) {
    break;
  }
}

continue arrête l’exécution de l’itération actuelle et commence l’exécution de l’itération suivante.

for (int i = 1 ; i < 10; ++i) {
  if (i % 2 == 0) {
    continue;
  }
  System.out.println(i);
}

libellé

Il est possible de mettre un libellé avant une expression for ou while. La seule et unique raison d’utiliser un libellé est le cas d’une itération imbriquée dans une autre itération. Par défaut, break et continue n’agissent que sur le bloc d’itération dans lequel ils apparaissent. En utilisant un libellé, on peut arrêter ou continuer sur une itération de niveau supérieur :

int m = 0;

boucleDeCalcul:
for (int i = 0; i < 10; ++i) {
  for (int k = 0; k < 10; ++k) {
    m += i * k;
    if (m > 500) {
      break boucleDeCalcul;
    }
  }
}

System.out.println(m);

Dans l’exemple ci-dessus, boucleDeCalcul est un libellé qui permet de signifier que l’instruction break porte sur la boucle de plus haut niveau. Son exécution stoppera donc l’itération des deux boucles et passera directement à l’affichage du résultat sur la sortie standard.

switch

Un expression switch permet d’effectuer une sélection parmi plusieurs valeurs.

switch (s) {
  case "valeur 1":
    // instructions
    break;
  case "valeur 2":
    // instructions
    break;
  case "valeur 3":
    // instructions
    break;
  default:
    // instructions
}

switch évalue l’expression entre parenthèses et la compare dans l’ordre avec les valeurs des lignes case. Si une est identique alors il commence à exécuter la ligne d’instruction qui suit. Attention, un case représente un point à partir duquel l’exécution du code commencera. Si on veut isoler chaque cas, il faut utiliser une instruction break. Au contraire, l’omission de l’instruction break peut être pratique si on veut effectuer le même traitement pour un ensemble de cas :

switch (c) {
  case 'a':
  case 'e':
  case 'i':
  case 'o':
  case 'u':
  case 'y':
    // instruction pour un voyelle
    break;
  default:
    // instructions pour une consonne
}

On peut ajouter une cas default qui servira de point d’exécution si aucun case ne correspond.

Note

Par convention, on place souvent le cas default à la fin. Cependant, il agit plus comme un libellé indiquant la ligne à laquelle doit commencer l’exécution du code. Il peut donc être placé n’importe où :

switch (c) {
  default:
    // instructions pour une consonne
  case 'a':
  case 'e':
  case 'i':
  case 'o':
  case 'u':
  case 'y':
    // instructions pour les consonnes et les voyelles
}

Prudence

En Java, le type d’expression accepté par un switch est limité. Un switch ne compile que pour un type primitif, une énumération ou une chaîne de caractères.

Exercices

Exercice - La classe BoiteDeVitesses

On souhaite créer une classe BoiteDeVitesses pour représenter une boite de vitesses. Cette classe contient les méthodes :

changerVitesse()

Pour passer à la vitesse supérieure. Il n’est pas possible d’aller au delà de la cinquième vitesse (dans ce cas, un appel à la méthode doit être sans effet).

retrograder()

Pour passer à la vitesse inférieure. Il n’est pas possible de rétrograder en dessous de la première (dans ce cas, un appel à la méthode doit être sans effet).

setPointMort()

Pour aller au point mort.

getVitesse()

Pour donner la vitesse courante (0 représente le point mort).

toString()

Qui retourne une chaîne de caractères (String). Si la vitesse vaut 0, la méthode retourne « point mort ». Pour les autres vitesses, la méthode retourne respectivement : « première », « seconde », « troisième », « quatrième », « cinquième ».

Ajoutez une méthode main pour créer une boite de vitesses et tester un appel aux méthodes. Testez notamment que les contraintes pour les méthodes changerVitesse() et retrograder() sont bien implémentées.

Exercice - Détecteur de température

On souhaite implémenter une classe DetecteurTemperature. Cette classe recevra des informations sur la température et sera capable de déduire si le niveau de température est normal, élevé ou critique :

Température

Niveau

t ≤ 30

normal

30 < t < 38

élevé

t ≥ 38

critique

La classe DetecteurTemperature possède les méthodes suivantes :

setTemperature(float t)

Pour positionner la température actuelle.

getNiveau()

Qui retourne le niveau de la température actuelle sous la forme d’une chaîne de caractères : « normal », « élevé », « critique »

isNiveauAtteint(String niveau)

Qui retourne true ou false si le niveau courant est au moins le niveau donné en paramètre.

Ajouter la méthode main suivante pour tester votre classe :

public static void main(String[] args) {
  DetecteurTemperature detecteurTemperature = new DetecteurTemperature();

  for(float temperature = 25; temperature < 45; temperature += 2) {
    detecteurTemperature.setTemperature(temperature);
    System.out.println("Température actuelle    = " + temperature);
    System.out.println("Niveau actuel           = " + detecteurTemperature.getNiveau());
    System.out.println("Niveau normal atteint   = " + detecteurTemperature.isNiveauAtteint("normal"));
    System.out.println("Niveau élevé atteint    = " + detecteurTemperature.isNiveauAtteint("élevé"));
    System.out.println("Niveau critique atteint = " + detecteurTemperature.isNiveauAtteint("critique"));
    System.out.println("###################################");
  }
}

Exercice - Implémenter le code de César

Implémenter le code de César en utilisant le code ci-dessous. Cet algorithme de chiffrement classique, utilisé par Jules César dans ses correspondances secrètes, se base sur le principe du décalage. Une lettre est remplacée par son équivalent dans l’alphabet en effectuant un décalage. Le programme ci-dessous utilise un décalage de 23. Cela donne comme équivalence :

  • a → x

  • b → y

  • c → z

  • d → a

  • e → b

  • f → c

  • g → d

  • h → e

  • i → f

  • j → g

  • k → h

  • l → i

  • m → j

  • n → k

  • o → l

  • p → m

  • q → n

  • r → o

  • s → p

  • t → q

  • u → r

  • v → s

  • w → t

  • x → u

  • y → v

  • z → w

Ainsi la phrase :

In cryptography, a Caesar cipher is one of the simplest and most widely known encryption techniques.

sera encodée avec un décalage de 23 comme ceci :

Fk zovmqldoxmev, x Zxbpxo zfmebo fp lkb lc qeb pfjmibpq xka jlpq tfabiv hkltk bkzovmqflk qbzekfnrbp.

Le programme suivant vérifie que le résultat du chiffrement est bien conforme au ce qui est attendu. Dans ce cas, il affichera true sur la sortie standard.

/**
 * Un exemple d'implémentation du Code César.
 *
 * Pour plus d'info, vous pouvez consulter
 * l'<a href="https://fr.wikipedia.org/wiki/Chiffrement_par_d%C3%A9calage">
 * article de Wikipedia</a>.
 *
 * @author David Gayerie
 *
 */
public class AlgoCesar {

  public String encrypte(String s) {
    // TODO
  }

  public static void main(String[] args) {
    AlgoCesar algoCesar = new AlgoCesar();

    String resultat = algoCesar.encrypte("");
    System.out.println("".equals(resultat));

    // étape 2
    /*
    resultat = algoCesar.encrypte("az");
    System.out.println("xw".equals(resultat));
    */

    // étape 3
    /*
    resultat = algoCesar.encrypte("AZ");
    System.out.println("XW".equals(resultat));
    */

    // étape 4
    /*
    resultat = algoCesar.encrypte("1,000.00");
    System.out.println("1,000.00".equals(resultat));
    */

    // étape 5
    /*
    String phrase = "In cryptography, a Caesar cipher is one "
                    + "of the simplest and most widely known "
                    + "encryption techniques.";
    resultat = algoCesar.encrypte(phrase);

    String phraseAttendue = "Fk zovmqldoxmev, x Zxbpxo zfmebo fp lkb "
                            + "lc qeb pfjmibpq xka jlpq tfabiv hkltk "
                            + "bkzovmqflk qbzekfnrbp.";
    System.out.println(phraseAttendue.equals(resultat));
    */
  }
}

Décommentez le code de chaque étape dans la méthode main au fur et à mesure que vous implémentez la méthode encrypte afin de vérifier que votre algorithme fonctionne correctement.

Indication

On peut obtenir un tableau de caractères à partir d’une chaîne avec la méthode toCharArray. S’il n’est pas possible de parcourir les éléments d’une chaîne de caractères avec un for amélioré, on peut facilement parcourir le tableau de caractères :

String helloWorld = "Hello world!";
for (char c : helloWorld.toCharArray()) {
  // ...
}

Il est possible de créer une nouvelle chaîne de caractères à partir d’un tableau :

char[] tableau = "Hello".toCharArray();
String chaine = new String(tableau);