Très pratique car permet d'écrire du code plus lisible, non pollué par la gestion des cas d'erreur.
Le mécanisme des exceptions utilise 5 mots-clés du langage :
try
, catch
, finally
, throw
et throws
.
Sans les exceptions :
fonction ContenuDuFichier(String filename){ ouvrirFichier(filename) si erreur d'ouverture return null String contentu = lireFichier Si erreur de lecture return null return contenu }Avec les exceptions :
fonction ContenuDuFichier(String filename){ try{ ouvrirFichier(filename) String contentu = lireFichier return contenu } catch(ErreurOuverture e){ return null } catch(ErreurLecture e){ return null } }Le programme s'exécute ; si une instruction déclenche une exception, l'exécution s'arrête et continue dans le bloc catch correspondant au type d'exception.
En plus de
try
et catch
, on peut définir un bloc finally
, qui va s'exécuter dans tous les cas, qu'une exception ait été lancée ou pas.
Le bloc
finally
est exécuté même si une exception est propagée ; utile par exemple pour fermer une connexion réseau ou à une base de données.
catch
et finally
sont optionnels, mais au moins un des deux est nécessaire.
Les accolades sont obligatoires dans ces blocs, même s'ils ne contiennent qu'une ligne.
try { // instructions } catch(ExceptionType1 e){ // traitement de ce cas anormal de type ExceptionType1 // e est de type ExceptionType1 } catch(ExceptionType2 e){ // traitement de ce cas anormal de type ExceptionType2 // e est de type ExceptionType2 } finally { traitement de fin de bloc try }Une même clause
catch
peut gérer plusieurs types d'exception, en les séparant par |
.
try{ ... } catch(ExceptionType1 | ExceptionType2 e){ // la variable e est un objet de type ExceptionType1 ou ExceptionType2 }
Les classes d'exception
Les exceptions que l'on peut lancer ou attrapper sont des objets de typejava.lang.Exception
ou de ses sous-classes.
L'API java fournit un grand nombre de classes :
On distingue 2 types principaux d'exceptions :
- les exceptions vérifiées (checked exceptions), qui peuvent être anticipées et doivent être gérées par l'application.
- les exceptions non vérifiées (unchecked exceptions), qui ne peuvent pas être anticipées (par ex
OutOfMemoryError
).
Les unchecked exceptions descendent de
Error
ou de RuntimeException
.
Une exception contient un message, accessible via
getMessage()
.
printStackTrace()
est aussi courrament utilisée.
Créer ses propres exceptions
On peut créer des exceptions personnalisées en sous-classantException
:
public MyException extends Exception {}Dans le code qu'on écrit on a le choix entre utiliser
Exception
et ses sous-classes lorsque c'est pertinent, ou les classes d'exceptions spécifiques à l'application.
Lancer des exceptions
Une exception est "lancée" avec l'instructionthrow
lorsqu'une condition anormale d'exécution est rencontrée :
public static double factorial(int x) throws IllegalArgumentException{ if(x < 0){ throw new IllegalArgumentException("x must be >= 0"); } double fact; for(fact=1.0; x > 1; fact *= x, x--) ; // instruction vide return fact; }(code dans Factorial.java)
Propagation des exceptions
Si l'interpréteur java rencontre une exception, il arrête l'exécution du code et cherche un gestionnaire d'exception (un bloccatch
).
L'interpréteur java va remonter la pile d'appel des méthodes jusqu'à ce qu'il trouve un bloc
catch
correspondant au type de l'exception en cours ou à une superclasse.
Par exemple, si on se place dans une méthode qui utilise
factorial()
:
void maMéthode(){ ... double f = factorial(-5); ... }- Soit
maMéthode()
contient un bloc try catch gérant IllegalArgumentException
void maMéthode(){ try{ ... double f = factorial(-5); ... } catch(IllegalArgumentException e){ // gestion de e } }Dans ce cas le code présent dans ce bloc catch sera exécuté.
- Soit
maMéthode()
ne gère pas IllegalArgumentException
et doit alors obligatoirement déclarer qu'elle est susceptible de lancer une exception avec le mot-clé throws
:
void maMéthode throws IllegalArgumentException(){ ... double f = factorial(-5); ... }Dans ce cas, l'exception sera propagée à la méthode qui a appelé
maMéthode()
, ainsi de suite jusqu'à la fonction main()
.
Donc en général, le code d'une fonction
main()
contient un try catch :
public static void main(String[] args) { try{ } catch(Exception e){ }Dans ce cas, on a intérêt à utiliser la classe
Exception
, qui est la superclasse de toutes les autres exceptions, çe qui permet de rattrapper toute erreur imprévue dans le programme, quelque soit son type.
A noter : les blocs
catch
sont évalués les uns après les autres. Le compilateur impose donc que les cas particuliers soient traités d'abord.
void maMéthodeQuiNePassePasALaCompil(){ try{ ... double f = factorial(-5); ... } catch(Exception e){ } catch(IllegalArgumentException e){ } }Si ce code passait à la compilation, le
catch
de IllegalArgumentException
ne serait jamais appelé puisque c'est une sous-classe de Exception
.
Exercice Multiples2