La gestion des exceptions
Objectifs
Comprendre le mécanisme des exceptions et son utilisation
Comprendre les différents types d'erreur en Java
La gestion des exceptions et la hiérarchie des exceptions en Java
Mise en situation
Lorsque nous sommes confrontés à un comportement exceptionnel d'une application (le plantage ou le crash de l'application), le langage Java nous offre un mécanisme nous permettant de gérer la situation avec élégance afin de donner à un utilisateur une expérience toujours positive de l'application.
Gestion d'exception
Les exceptions représentent le mécanisme de gestion des erreurs intégré du langage Java.
Il est composé d'objets représentant les erreurs et d'un ensemble de trois mots-clés qui permettent de détecter et de traiter ces erreurs (try, catch, finally) mais aussi de les lever ou de les propager (throw, throws).
Parmi les raisons fréquentes qui produisent ces types d'erreurs, on retrouve souvent :
Les données non valides entrées par un utilisateur
Un fichier introuvable que le programme doit utiliser
Une connexion réseau perdue ou en panne en plein milieu d'une communication, un système de fichiers corrompu ou encore la JVM en manque de mémoire.
Exigence et types d'exceptions
Les méthodes d'un programme Java robuste et valide qui peuvent lever certaines exceptions doivent respecter les exigences capture des exceptions ou de la spécification des exceptions.
Être entourées par un bloc try/catch permettant de capturer des exceptions afin de les traiter (capture)
Ou ajouter le mot-clé throws dans leur déclaration qui spécifie l’exception à gérer (spécification).
Toutes les exceptions ne sont pas soumises à l'exigence de capture ou de spécification. On distingue trois grandes catégories d'exception dont une seule est soumise à l'exigence.
Les exceptions vérifiées
Les exceptions non vérifiées/exceptions d'exécution
Les erreurs
1. Les exceptions vérifiées/les exceptions de compilation
Ce sont des exceptions qu'une application bien rédigée doit anticiper et surmonter. On doit soit les propager, soit les gérer nous-mêmes.
Exemple : Un fichier texte introuvable par un programme
Par exemple, supposons qu'une application demande à un utilisateur un nom de fichier d'entrée pour pouvoir l'utiliser après.
Normalement, l'utilisateur fournit le nom d'un fichier existant et lisible. Mais parfois, l'utilisateur fournit le nom d'un fichier inexistant (fichierInexistant.txt).
import java.io.FileReader;
public class FileNotFoundStudi {
public static void main(String args[]) {
File file = new File("C://fichierInexistant.txt");
FileReader fr = new FileReader(file);
}
}
C:\>javac FileNotFoundStudi.java
java: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
FileReader fr = new FileReader(fichierInexistant.txt);
^
1 error
Ici, sur la console, Java suggère comment surmonter le problème du plantage brutal du programme en raison de l'omission de la ressource de fichier que le programme tente d'ouvrir en vain.
Un programme bien écrit interceptera cette exception et informera l'utilisateur de l'erreur, demandant éventuellement un nom de fichier corrigé.
Lorsque ces exceptions surviennent, le compilateur Java nous demande de gérer. Nous devons soit lancer l'exception de manière déclarative dans la pile d'appels, soit la gérer nous-mêmes.
La plupart des classes et méthodes intégrées au langage Java sont codées de façon à gérer les exceptions qu'elles peuvent générer.
Les exceptions vérifiées/de compilations sont soumises à l'exigence de capture ou de spécification.
Quelques exemples d'exceptions vérifiées sont IOException et ServletException, FileNotFoundException...
2. Les exceptions non vérifiées/RuntimeException
Ce sont des exceptions qui sont internes au programme, et que le programme ne peut généralement pas anticiper ou surmonter.
Il n'est pas nécessaire de les manipuler. On doit donc les corriger manuellement ou enlever les codes causant ces exceptions.
Ce sont des bogues de programmation tels que des erreurs de logique, la mauvaise utilisation d'une API...
Exemple : Transmission d'un objet null comme fichier
Par exemple, considérons l'application décrite précédemment. Si nous transmettons un objet null au lieu d'un fichier, nous obtenons une erreur décrite comme suit :
import java.io.FileReader;
public class FileNotFoundStudi {
public static void main(String args[]) throws FileNotFoundException {
File file = null;
FileReader fr = new FileReader(file);
}
}
C:\>javac FilenotFoundStudi.java
java: Exception in thread "main" java.lang.NullPointerException
FileReader fr = new FileReader(file);
^
1 error
Ici on transmet un objet null au fichier file
qui attend un objet de type File. Ceci cause une exception.
L'application peut intercepter cette exception (ici NullPointerException), mais il est probablement préférable d'éliminer le bogue à l'origine de l'exception.
Les exceptions d'exécution ne sont pas soumises à l'exigence Catch ou Specify. Les exceptions d'exécution sont celles indiquées par RuntimeException et ses sous-classes.
Quelques exemples d'exceptions non vérifiées sont NullPointerException, IllegalArgumentException et SecurityException.
3. Les erreurs
Ce sont des exceptions qui sont externes au programme, et que le programme lui-même ne peut généralement pas anticiper ou récupérer.
En d'autres mots, les erreurs représentent des conditions graves et généralement irrécupérables comme une incompatibilité de bibliothèque, une récursion infinie ou des fuites de mémoire.
Exemple : Un fichier texte corrompu et impossible à lire
Par exemple, supposons qu'une application récupère avec succès un fichier en entrée mais que le programme n'arrive pas à lire le fichier à cause d'un dysfonctionnent matériel ou système.
Un programme peut choisir d'intercepter cette exception afin d'avertir l'utilisateur du problème ou juste afficher le message d'erreur sur la console et s'arrête après.
Dans la plupart des cas, il serait étrange de gérer, d'instancier ou d'étendre les erreurs. Habituellement, nous voulons que celles-ci se propagent tout le long.
Les erreurs ne sont pas soumises à l'exigence de capture ou de spécification. Elles sont les exceptions indiquées par Error et ses sous-classes.
Quelques exemples d'erreurs sont StackOverflowError et OutOfMemoryError, IOError...
Toutes les exceptions sont vérifiées sauf celles indiquées par Error, RuntimeException et leurs sous-classes.
Hiérarchie des exceptions/La classe Throwable
Les exceptions ne sont que des objets Java, tous héritent de la classe Throwable :
Toutes les classes d'exception sont des sous-types de la classe java.lang.Exception. La classe d'exception est une sous-classe de la classe Throwable. Outre la classe d'exception, il existe une autre sous-classe appelée Error qui est dérivée de la classe Throwable. Le schéma ci-contre indique l'arborescence des différents types d'erreurs.
Méthode : Les méthodes d'exception
Voici la liste des méthodes importantes disponibles dans la classe Throwable. Ces méthodes sont très utiles pour la gestion des exceptions après leurs captures.
Méthodes | Description |
public String getMessage( ) | Renvoie un message détaillé sur l'exception qui s'est produite. Ce message est initialisé dans le constructeur Throwable. |
public Throwable getCause( ) | Renvoie la cause de l'exception représentée par un objet Throwable. |
public String toString( ) | Renvoie le nom de la classe concaténée avec le résultat de getMessage(). |
public void printStackTrace( ) | Imprime le résultat de toString() avec la trace de la pile dans System.err, le flux de sortie d'erreur. |
public StackTraceElement [] getStackTrace( ) | Renvoie un tableau contenant chaque élément de la trace de pile. L'élément à l'index 0 représente le haut de la pile d'appels et le dernier élément du tableau représente la méthode au bas de la pile d'appels. |
public Throwable fillInStackTrace( ) | Remplit la trace de pile de cet objet Throwable avec la trace de pile actuelle, en ajoutant toutes les informations précédentes dans la trace de pile. |
Syntaxe : À retenir
Dans ce chapitre, les points importants à retenir sont :
Les méthodes d'un programme Java robuste et valide qui peuvent lever certaines exceptions doivent respecter les exigences Capture (try, catch, finally) des exceptions ou de la spécification (throws/throw) des exceptions.
Les exceptions vérifiées/de compilation : ce sont des exceptions vérifiées par le compilateur au moment de la compilation et le programmeur est invité à gérer ces exceptions.
Les exceptions non vérifiées/RuntimeException : les exceptions non vérifiées ne sont pas vérifiées au moment de la compilation, mais elles sont vérifiées à l'exécution.
Les erreurs/Error : ce sont des exceptions qui sont externes au programme, et que le programme lui-même ne peut généralement pas anticiper ou récupérer.
Toutes les exceptions sont vérifiées sauf celles indiquées par Error (les erreurs), RuntimeException (erreurs d'exécutions) et leurs sous-classes.
La hiérarchie des exceptions où l'on voit que toutes les exceptions sont des sous-classes de la classe Throwable.