Accès rapide :
La vidéo
La hiérarchie des classes d'exceptions
Erreurs, exceptions et « unchecked exceptions »
Quelques classes d'exceptions ou d'erreurs
Quelques méthodes utilisables sur les exceptions.
Utilisation du « multi-catch »
Utilisation du rethrow
Cette vidéo présente quelques aspects complémentaires liés au traitement d'exception en Java et notamment : la hiérarchie de classes d'exception, le multi-catch et le rethrow d'exception.
Comme vous vous en êtes aperçu dans les exemples précédents, il existe un certain nombre de classes d'exceptions prédéfinies.
Pour être plus précis, les principales classes d'exceptions sont localisées sans le package java.lang
(par exemple
java.lang.ArithmeticException
ou java.lang.SecurityException
), mais ensuite, chaque package complète ces
classes d'erreur avec ses exceptions propres. Par exemple le package java.io
, pour la gestion des entrées/sorties, propose ses propres classes
d'exceptions qui dériveront toutes de java.io.IOEXception
.
Vous vous serez aussi aperçue que ces classes sont reliées les unes aux autres par des liens d'héritage. Le diagramme suivant présente la racine de cette
hiérarchie de classe d'exceptions. On y voit très rapidement qu'il y a deux branches principales : les erreurs (dérivant de java.lang.Error
et
coloriées en rouge) et les exceptions (dérivant de java.lang.Exception
, coloriées en vert et bleu).
Ces deux types dérivant eux-mêmes de java.lang.Throwable
.
Les erreurs (dérivant de java.lang.Error
) représente des erreurs d'exécution dans la JVM. Autant vous dire que si vous interceptez ces types
d'objets dans un bloc try / catch
, vous ne pourrez pas y faire grand-chose. Les deux classes d'erreurs les plus connues sont
java.lang.OutOfMemoryError
(plus de possibilité de faire des new
) et java.lang.StackOverflowError
(vous avez saturé la pile d'exécution).
Les exceptions correspondent à des erreurs générées durant l'exemple de vos programmes, telles que des problèmes d'accès à des fichiers, ou autre.
Notez néanmoins que j'ai coloré en vert la branche de classes dérivant de java.lang.RuntimeException
: c'est ce qu'on appelle les
« unchecked exceptions ». Ces exceptions, pouvant intervenir sur quasiment toutes vos lignes de code (par exemple,
java.lang.NullPointerException
), elles n'ont pas d'obligation d'être traitées et sont relayées à la méthode appelante par défaut.
Dans l'exemple relatif à la copie de fichier sans bloc catch
, j'avais défini le mail
ainsi :
1 2 3 4 5 |
public static String main( String [] args ) throws IOException { // Suite du main. } |
Or, le constructeur de la classe FileInputStream
peut aussi produire des exceptions de type java.lang.SecurityException
.
Alors pourquoi cette classe (qui ne dérive pas de java.io.IOException
) n'est pas mentionnée dans la définition du main
et pourquoi
le programme compile sans erreur ?
La réponse est simple : par ce qu'elle dérive de java.lang.RuntimeException
. Elle est donc remontée par défaut.
Pour autant, la déclaration suivante serait légale.
1 2 3 4 5 |
public static String main( String [] args ) throws IOException, SecurityException { // Suite du main. } |
Il est à noter que dans une Javadoc, les classes, les exceptions et les erreurs sont regroupées dans des catégories distinctes. Voici une capture d'écran montrant le contenu d'un package.
Le tableau suivant vous présente quelques classes d'exceptions relativement courante que vous pourriez être amené à traiter.
Nom de la classe | Description |
---|---|
java.lang.Throwable | Classe de base de toutes erreurs ou toutes exceptions. |
java.lang.Error | Classe de base de toutes erreurs. |
java.lang.OutOfMemoryError |
Vous avez saturé le tas (le heap en anglais) qui est l'espace mémoire dans lequel on produit les objets.
La taille de cet espace peut être contrôlé via l'option -Xmx (MaXimum size) de la JVM.
|
java.lang.StackOverflowError |
Vous avez saturé la pile (la stack en anglais) qui est l'espace mémoire dans laquelle vos variables locales sont produites.
La taille de cet espace peut être contrôlé via l'option -Xss (Stack Size) de la JVM.
|
java.lang.Exception | Classe de base de toutes exceptions. |
java.lang.RuntimeException | Classe de base de toutes « unchecked exceptions ». |
java.lang.ArithmeticException | La division par zéro représente le cas le plus courant de déclenchement de cette exception. |
java.lang.ArrayIndexOutOfBoundsException | Une telle exception est déclenchée si votre indice dépasse des bornes du tableau |
java.lang.ClassCastException | Indique que votre cast n'est pas légal. |
java.lang.NullPointerException | Souvent abrégée en NPE, cette exception indique que vous avez manipulé un pointeur nul. |
java.lang.SecurityException | Indique qu'un problème de sécurité a été rencontré. |
java.io.IOException | Représente une erreur de manipulation d'un flux d'entrée/sortie. |
java.net.SocketException | Représente une erreur de manipulation d'une socket réseau. Cette classe dérive de java.io.IOException. |
java.sql.SQLException | Représente une erreur de manipulation d'une base de données relationnelle (basée sur SQL). |
La classe java.lang.Throwable
expose un certain nombre de méthodes que vous pouvez utiliser sur n'importe quelle exception ou erreur.
Le tableau suivant vous présente les principales méthodes.
Nom de la méthode | Description |
---|---|
String getMessage(); | Retourne le message de l'exception (sur une seule ligne) |
StackTraceElement [] getStackTrace(); | Retourne l'ensemble des appels (Stack Trace) ayant amené à produire cette exception |
Throwable getCause(); | Retourne l'erreur d'origine ayant produit l'erreur constatée. Effectivement, quand vous construisez une exception, il est possible d'indiquer au constructeur une exception d'origine. |
void printStackTrace(); | Affiche sur la sortie standard d'erreur le message de l'exception ainsi que la trace des appels de méthodes. |
void printStackTrace( PrintStream outputStream ); | Injecte, dans un flux 8 bits (dérivé de java.io.OutputStream ) le message de l'exception ainsi que la trace des appels de méthodes. |
void printStackTrace( PrintWriter writer ); | Injecte, dans un flux 16 bits (dérivé de java.io.Writer ) le message de l'exception ainsi que la trace des appels de méthodes. |
Le Java SE 7.0 a apporté une facilité pour attraper plusieurs types d'exceptions avec un seul bloc catch
: l'opérateur « multi-catch ».
Bien sûr, vous pouvez capturer plusieurs types d'exceptions par polymorphisme, en utilisant un type de base. Mais souvent cela englobe trop de types d'exceptions.
Le multi-catch pourra être bien plus précis.
Pour définir qu'un bloc catch
correspond à plusieurs types d'exception, il faut utiliser l'opérateur |
(touches ALT-GR + 6).
Vous pouvez préciser autant de types d'exceptions que nécessaire. Voici un exemple d'utilisation.
1 2 3 4 5 6 7 8 9 10 11 |
try { // Quelques lignes de code pouvant déclencher : // * soit une exception de type SQLException // * soit une exception de type IOException } catch ( SQLException | IOException exception ) { // On intercepte ici une eventuelle exception de type SQLException ou IOException } |
catch
basé sur le type Exception
, mais dans ce cas on aurait
aussi intercepté les exceptions de type java.awt.AWTException
, javax.naming.NamingException.html
,
java.awt.print.PrinterException
, ... Le multi-catch cible donc mieux les exceptions à traiter.
Mais du coup, une question se pose : quels sont les membres (attributs, méthodes, ...) que l'on peut manipuler sur l'instance exception
du bloc
catch
? La réponse est simple, seuls les membres accessibles sur la première classe commune à nos exceptions seront utilisables.
Concrètement, dans notre exemple, seuls les membres définis sur java.lang.Exception
(la classe mère de nos deux types) seront accessibles.
Parmi eux : toString
, getMessage
, printStackTrace
, ...
Le rethrow consiste à relancer une exception suite à son traitement partiel dans un bloc catch
.
Voici un premier exemple simple de rethrow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void aMethod() throws Exception { try { // Faire quelque chose qui peut déclencher une exception } catch ( Exception exception ) { // Faire un traitement partiel de l'erreur. // puis relancer l'exception au niveau supérieur : throw exception; } } |
Mais une nouvelle question se pose : si une instruction try / catch
capture plusieurs types exceptions (via divers blocs catch
, ou un
« multi-catch ») et qu'on relance les exceptions interceptées, comment définir la signature de la méthode englobante ?
Une première solution consiste à utiliser le premier type parent commun aux diverses exceptions potentiellement attrapables. L'avantage de cette solution est que cela marche même avec un vieux Java SE (inférieur à 7.0). Voici un exemple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public void aMethod() throws Exception { try { // Faire quelque chose qui peut déclencher divers types d'exceptions } catch ( IOException exception ) { // Faire un traitement partiel de l'erreur. // puis relancer l'exception au niveau supérieur : throw exception; } catch ( SQLException exception ) { // Faire un autre traitement partiel de l'erreur. // puis relancer l'exception au niveau supérieur : throw exception; } } |
Une autre solution, consiste à utiliser les nouvelles possibilités offertes à partir du Java SE 7.0. Il n'est plus obligatoire de typer la signature de la méthode via un type d'exception commun et l'on peut désormais lister tous les types d'exceptions potentiellement relancés. Voici un exemple de code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void aMethod() throws IOException, SQLException { try { // Faire quelque chose qui peut déclencher divers types d'exceptions } catch ( IOException | SQLException exception ) { // Faire un traitement partiel de l'erreur. // puis relancer l'exception au niveau supérieur : throw exception; } } |
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :