• 15 heures
  • Facile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 19/05/2021

Gérez les comportements inattendus avec les erreurs et exceptions

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Qu'est-ce une erreur ?

Les erreurs sont des problèmes de code. Elles peuvent aller de fautes de frappe à l'utilisation incorrecte de variables ou de fonctions qui peuvent être repérées immédiatement, en passant par des erreurs qui surviennent pendant l'exécution du programme. Elles dépendent souvent de la séquence d'exécution et des résultats du code précédemment exécuté.

Examinons comment créer et utiliser une application. Vous l'écrivez dans un langage de programmation. Ce code est traduit en code machine et il est ensuite exécuté au fur et à mesure que vous utilisez une application.

Voici les deux phases de ce processus :

  • compilation (ou interprétation dans certaines langues) – c'est là que le code est vérifié et traduit en code machine. Le résultat du processus de compilation est un fichier qui est prêt à être exécuté et appelé – exécutable ;

  • exécution – exécuter l'application – exécuter le fichier exécutable !

Chacune des phases peut avoir son propre ensemble d'erreurs.

Repérez et corrigez les erreurs de compilation

Des erreurs de compilation peuvent être détectées par le compilateur Java (javac), pendant le processus de traduction de votre code Java lisible par un être humain, en code byte lisible par une machine. Ces erreurs peuvent être syntaxiques (fautes d'orthographe) ou sémantiques, telles que l'utilisation de mots interdits, de code non interprétable, de types incorrects, etc.

Si vous utilisez un environnement de développement intégré (IDE) comme Eclipse ou IntelliJ IDE, vous serez averti des erreurs en direct lorsque vous taperez votre code. Les lignes de code qui correspondent à des erreurs de compilation sont surlignées en rouge. Ce sont des erreurs dites « faciles », car elles peuvent (et doivent) être corrigées sur place. L'application ne peut pas être compilée tant qu'il y a des erreurs de compilation. Mais elles sont faciles à réparer, et vous pouvez voir exactement ce qui ne va pas.

Les erreurs d'exécution, quant à elles, peuvent être difficiles à corriger.

Gérez les erreurs d'exécution

Les erreurs d'exécution surviennent pendant le processus d'exécution, après le lancement de l'application. Elles n'apparaissent que pendant le processus d'exécution, et il n'y a aucun moyen de les détecter pendant la compilation.

Ce type d'erreur est typiquement lié à la logique du code, comme l'accès à des données qui n'existent pas ou qui existent dans un format différent, ou à l'exécution d'une action non prise en charge, etc.

Par exemple, si vous essayez d'accéder à un élément d'une liste dont l'index est supérieur à la longueur de cette liste, il y a une erreur de logique : vous pensiez que la liste serait plus longue.

Un exemple d'erreur de logique métier pourrait être que dans une application financière, vous avez inversé la signification des termes crédit et débit (ce qui n’est pas du tout OK). Cependant, l'application parviendra quand même à fonctionner dans cette situation. Elle va simplement produire tout le contraire du résultat que vous attendez ! Alors, ne les mélangez pas ! 😉

En général, une erreur logique a pour principale conséquence un plantage de l'application.

Certaines de ces erreurs peuvent facilement être reproduites au fur et à mesure qu'elles surviennent. D'autres ne se produisent qu'occasionnellement. Elles sont plus difficiles à résoudre car elles sont généralement plus difficiles à reproduire. Les erreurs occasionnelles sont habituellement liées à des conditions particulières du code. Voici un exemple de ce genre d'erreur ninja dans la structure du code :

if(weekDay) {
//exécuter le code sans erreur
}
else {
//exécuter le code avec des erreurs
}

Dans l'exemple ci-dessus, si vous travaillez sur votre projet uniquement du lundi au vendredi et que vous obtenez tous les plantages signalés pendant le week-end, il ne sera pas évident au début de savoir pourquoi votre application plante.

Alors, qu'est-ce que vous pouvez y faire ?

Dans certains cas, vous pouvez déboguer votre programme et détecter ces points faibles.

Vous pouvez généralement utiliser deux stratégies :

  1. Recherche d'erreurs à l'aide de conditions.

  2. Tirer parti du mécanisme des exceptions.

Revoyons chaque stratégie avec un exemple courant : la fameuse erreur de la division par zéro.

Recherchez les erreurs

Créez d'abord une classe utilitaire dans un package  exceptions, appelé  SimpleMaths, qui fournit une méthode  calculateAverage  qui calcule la moyenne d'une liste de valeurs entières.

Sinon, vous pourrez vous entraîner dans l'exercice interactif à la fin de ce chapitre.

package exceptions;
import java.util.List;
public class SimpleMaths {
/** calculez la valeur moyenne d'une liste d'entiers
*
* @param listOfIntegers une liste contenant des nombres entiers
* @return la moyenne de la liste
*/
public static int calculateAverage(List<Integer> listOfIntegers) {
int average=0;
for (int value: listOfIntegers) {
average+=value;
}
average/=listOfIntegers.size();
return average;
}
}

Définissez ensuite un programme dans une classe nommée  TemperatureAverage  à l'intérieur du même paquet d'exceptions qui fait usage de cette fonctionnalité. Ce programme reçoit les valeurs de température comme arguments en ligne de commande. Il appelle la fonction  calculateAverage  et affiche le résultat.

package exceptions;
import java.util.ArrayList;
import java.util.List;
public class TemperatureAverage {
/** affiche la température moyenne à partir des valeurs fournies comme arguments en ligne de commande
*
* @param args liste de températures séparées par des espaces
*/
public static void main(String[] args) {
List<Integer> recordedTemperaturesInDegreesCelcius = new ArrayList<Integer>();
// remplissez la liste à partir des valeurs fournies comme arguments en ligne de commande
for (String stringRepresentationOfTemperature : args) {
int temperature = Integer.parseInt(stringRepresentationOfTemperature);
recordedTemperaturesInDegreesCelcius.add(temperature);
}
// calculez et affichez la température moyenne
Integer averageTemperature = SimpleMaths.calculateAverage(recordedTemperaturesInDegreesCelcius);
System.out.println("The average temperature is " + averageTemperature);
}
}

Dans ce code, créez d'abord une liste vide, puis itérez sur le tableau  args  contenant les arguments de ligne de commande fournis. Puisque chaque argument est fourni sous la forme d'une  String  ; convertissez-le en  Integer  à l'aide de Integer.parseInt avant de l'ajouter à la liste. Une fois la liste complétée, appelez la fonction  calculateAverage  de la classe  SimpleMaths, affectez le résultat à la variable  averageTempérature  et imprimez sa valeur.

Compilons et exécutons ce code avec différents arguments :

$ javac exceptions/*.java $ java exceptions.TemperatureAverage 4 7 9 16
The average temperature is 9
$ java exceptions.TemperatureAverage 4 7
The average temperature is 5
$ java exceptions.TemperatureAverage
Exception in thread "main" java.lang.ArithmeticException: / by zero 
at exceptions.SimpleMaths.calculateAverage(SimpleMaths.java:17) 
at exceptions.TemperatureAverage.main(TemperatureAverage.java:23)

Sans surprise, vous avez des problèmes si vous ne fournissez aucun argument sur la ligne de commande, puisque vous ne pouvez pas diviser par zéro. Pour éviter cette erreur, ajoutez un contrôle dans la méthode  calculateAverage  :

if (listOfIntegers.size()===0){
return 0;
}

Toutefois, cette stratégie produirait un résultat très trompeur :  0  n'est pas un résultat valide. Nous n'avons même pas effectué la division ! Nous pouvons, cependant, régler la fonction  main  pour éviter d'appeler la fonction avec une liste vide :

package exceptions;
import java.util.ArrayList;
import java.util.List;
public class TemperatureAverageWithCheckForEmptyList {
/** affichez la température moyenne à partir des valeurs fournies comme arguments en ligne de commande
*
* @param args liste de températures séparées par des espaces
*/
public static void main(String[] args) {
List<Integer> recordedTemperaturesInDegreesCelcius = new ArrayList<Integer>();
// remplissez la liste à partir des valeurs fournies comme arguments en ligne de commande
for (String stringRepresentationOfTemperature : args) {
int temperature = Integer.parseInt(stringRepresentationOfTemperature);
recordedTemperaturesInDegreesCelcius.add(temperature);
}
// Protection contre la liste vide
if (recordedTemperaturesInDegreesCelcius.size() == 0) {
System.out.println("Cannot calculate average of empty list!");
} else {
// calculez et affichez la température moyenne
int averageTemperature =
SimpleMaths.calculateAverage(recordedTemperaturesInDegreesCelcius);
System.out.println("The average temperature is " + averageTemperature);
}
}
}

Voyons cela en action :

$ javac exceptions/*.java
$ java exceptions.TemperatureAverageWithCheckForEmptyList
Cannot calculate average of empty list!

Génial ! Vérifions avec d'autres arguments :

$ java exceptions.TemperatureAverageWithCheckForEmptyList 8 6 9 
The average temperature is 7 
$ java exceptions.TemperatureAverageWithCheckForEmptyList 8 6 eight2
Exception in thread "main" java.lang.NumberFormatException: For input string: "eight" 
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) 
at java.base/java.lang.Integer.parseInt(Integer.java:652) at java.base/java.lang.Integer.parseInt(Integer.java:770) 
at exceptions.TemperatureAverageWithCheckForEmptyList.main(TemperatureAverageWithCheckForEmptyList.java:18)

Il semble qu'il y ait un autre problème : notre méthode  parseInt  n'accepte pas "eight" comme entrée et lance une exception NumberFormatException qui fait planter le programme.

Intéressant ! Nous obtenons ce qu'on appelle un suivi de la pile : un message que Java imprime lorsqu'un programme plante. Il nous dit même ce qui s'est passé : java.lang.NumberFormatException: For input string: "eight."

Donc, si Java est capable d'imprimer ce message, pourrait-il empêcher le crash en tant que tel ?

Bien sûr que oui ! C'est à cela que sert le mécanisme de gestion des exceptions. 🙂

Gérez les exceptions

Le mécanisme de gestion des exceptions est le mécanisme de gestion des erreurs inclus par défaut dans Java. Il a pour fonction de faire un throw avec un événement qui interrompt le déroulement normal de l'exécution en cas de problème. Si cet événement est détecté, le problème peut être résolu. Sinon, le programme plante avec une trace de pile qui décrit ce qui s'est passé.

Pour cela, il existe un modèle de code général, try/catch (essayer/capturer). Cela signifie que vous demandez à faire quelque chose – exécuter un bloc de code, ou plutôt,  try  (essayer) de le faire. Si une erreur se produit, vous la capturerez :  catch.

Vous pouvez gérer l'erreur (ou les erreurs) dans la partie catch. Cela peut être aussi simple que de ne rien faire ou de réessayer (si votre logique métier le permet). En capturant une erreur (même si vous n'avez rien fait), vous avez empêché l'application de planter.

Voici à quoi ressemble cette construction en Java :

try {
// un peu de code
// une fonction à essayer pouvant générer une erreur
// encore du code
}
catch (ExceptionName e) {
// code à exécuter au cas où l'essai ne fonctionnerait pas et qu'une erreur se produirait
}

Que se passe-t-il si on essaie avec try ?

En cas de problème à l'intérieur d'une méthode pendant son exécution, une erreur est générée,  throw. Cette exception remonte jusqu'à la chaîne d'appels de méthode jusqu'à ce qu'elle soit capturée,  catch. Si aucune instruction d'interception n'est fournie, le programme finit par planter.

Corrigeons notre programme pour gérer nos sources d'erreurs connues :

package exceptions;
import java.util.ArrayList;
import java.util.List;
public class TemperatureAverageWithExceptionHandling {
/** affichez la température moyenne à partir des valeurs fournies comme arguments en ligne de commande
*
* @param args liste de températures séparées par des espaces
*/
public static void main(String[] args) {
try {
List<Integer> recordedTemperaturesInDegreesCelcius = new ArrayList<Integer>();
// remplissez la liste à partir des valeurs fournies comme arguments en ligne de commande
for (String stringRepresentationOfTemperature : args) {
int temperature = Integer.parseInt(stringRepresentationOfTemperature);
recordedTemperaturesInDegreesCelcius.add(temperature);
}
// calculez et affichez la température moyenne
int averageTemperature =
SimpleMaths.calculateAverage(recordedTemperaturesInDegreesCelcius);
System.out.println("The average temperature is " + averageTemperature);
} catch (NumberFormatException e) {
System.out.println("All arguments should be provided as numbers");
System.exit(-1);
} catch (ArithmeticException e) {
System.out.println("At least one temperature should be provided");
System.exit(-1);
}
}
}

Ce code est divisé en deux parties :

  • le flux normal, entre  try{  et }, est détecté : il se compose des instructions à exécuter tant qu'aucune erreur ne se produit ;

  • Un ensemble d'instructions  catch(Exception e). Si une erreur se produit et que l'exception qui est lancée correspond au type d'exception défini dans une instruction  catch, le bloc qui correspond à cette instruction catch est exécuté. Si aucune instruction de capture ne correspond, le programme plante et affiche la trace de la pile.

Ce mécanisme permet de séparer le flux normal d'un programme de la partie de traitement des erreurs. Il permet même de gérer une erreur de méthode dans la méthode appelante. Dans notre exemple, l'exception  ArithmeticException  est générée depuis la méthode  calculateAverage, mais interceptée dans la méthode  main.

À vous de jouer !

Console de code
Houston... ?
Il semblerait que votre ordinateur ne soit pas connecté à Internet.
Vous n'êtes pas connecté

Connectez-vous pour accéder aux exercices de codes et testez vos nouvelles compétences.

Pensez à vous entraîner avant de terminer ce chapitre.

En résumé

Dans ce chapitre, vous avez appris un certain nombre de concepts concernant les erreurs d'application :

  • la compilation est le processus de traduction du code d'un langage de programmation en code machine. Les erreurs de compilation sont faciles à découvrir et doivent être résolues pour terminer le processus de compilation ;

  • l'exécution est le processus d'utilisation ou d'exécution de l'application. Les erreurs d'exécution sont plus difficiles à découvrir et provoquent le crash d'une application ;

  • en Java, les erreurs d'exécution génèrent des exceptions ;

  • les exceptions sont gérées à l'aide d'une instruction try/catch :

    • Try  indique qu'une fonction peut lancer une exception,

    • Catch indique quel code exécuter si une exception est générée (throw).

Dans le chapitre suivant, nous traiterons de la communication avec l'utilisateur, ce qui nous permettra également d'approfondir les exceptions.

Exemple de certificat de réussite
Exemple de certificat de réussite