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.

  1. Les exceptions vérifiées

  2. Les exceptions non vérifiées/exceptions d'exécution

  3. 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.

ExempleUn 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).

1
import java.io.FileReader;
2
3
public class FileNotFoundStudi {
4
5
   public static void main(String args[]) {		
6
      File file = new File("C://fichierInexistant.txt");
7
      FileReader fr = new FileReader(file); 
8
   }
9
}
1
C:\>javac FileNotFoundStudi.java
2
java: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
3
      FileReader fr = new FileReader(fichierInexistant.txt);
4
                      ^
5
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...

ExempleTransmission 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 :

1
import java.io.FileReader;
2
3
public class FileNotFoundStudi {
4
5
   public static void main(String args[]) throws FileNotFoundException {		
6
      File file = null;
7
      FileReader fr = new FileReader(file); 
8
   }
9
}
1
C:\>javac FilenotFoundStudi.java
2
java: Exception in thread "main" java.lang.NullPointerException
3
      FileReader fr = new FileReader(file);
4
                                      ^
5
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.

ExempleUn 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éthodeLes 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.