Dans notre précédente
introduction aux exceptions en Java, nous avions jeté
les bases de ces mécanismes notamment à l'aide d'un
exemple comportant une division par zéro. Ce calcul provoquait
alors une exception, de type "ArithmeticException".
Nous reprenons aujourd'hui cet exemple en l'implémentant
afin d'en savoir un peu plus sur le bloc "try / catch / finally"
que nous avions abordé en fin du tutoriel précédent.
Catch multiples et bloc finally
Pour rappel, voici à quoi ressemblait dans notre exemple
:
(ZeroDivide.java)
public class ZeroDivide
{
static public void main(String[] args)
{
int
a = 3;
int
b = 0;
try
{
System.out.println("Resultat
de la division : " + a/b);
System.out.println("Instructions
suivant la division...");
}
catch(ArithmeticException
e)
{
System.out.println("Une
exception s'est produite ! (ArithmeticException)");
}
System.out.println("Instructions
qui suivent le bloc catch...");
}
}
Nous obtenions à l'affichage :
Une exception s'est produite ! (ArithmeticException)
Instructions qui suivent le bloc catch...
Voyons ce que donne un bloc "finally" avec ce même
exemple. L'intérêt d'un tel bloc repose sur le fait
que les instructions qu'il comporte seront toujours exécutées,
qu'une exception soit levée ou pas. Il permet donc de s'assurer
par exemple qu'un fichier ouvert dans le bloc try sera systématiquement
refermé, quoiqu'il arrive, grâce au bloc "finally".
Au vu des résultats fournis par l'exemple précédent
(le traitement continue après le bloc "catch"),
on peut se demander pourquoi ne pas mettre ces instructions de fermeture
de fichier (en prenant le même exemple) dans le bloc catch
?
En fait plusieurs blocs catch sont susceptibles de se suivre et
donc de correspondre au même bloc try. Il serait redondant
de placer son code de fermeture de fichier dans chacun des blocs
catch correspondant au bloc try...
A propos de ces blocs "catch" qui se suivent, il faut
prendre garde à l'ordre dans lequel on les place : toujours
du plus "particulier" au plus "général".
Admettons que nous possédions la configuration suivante
:
catch(ArithmeticException e)
{
...
}
catch(IndexOutofBounds e)
{
...
}
catch(Exception e)
{
...
}
Si les deux premiers catch peuvent être intervertis sans problème
(ils sont spécialisés dans un type d'erreur précis),
le dernier représente la classe Exception, il est
donc susceptible de recevoir la plupart des exceptions qui nous
intéressent !
De manière générale si la classe d'un catch
dérive de celle d'un autre catch, elle doit être placée
devant cette dernière.
Inutile de vivre dans l'angoisse d'oublier ce dernier principe,
le compilateur lui n'oublie pas, et saura vous le rappeler. Ainsi
dans l'exemple précédent, si le catch (Exception e)
n'est pas placé en dernier, le code ne se compilera pas.
En ce qui concerne notre bloc finally, sa syntaxe est des plus simples
:
try
{
System.out.println("Resultat
de la division : " + a/b);
System.out.println("Instructions
suivant la division...");
}
catch(ArithmeticException e)
{
System.out.println("Une exception
s'est produite ! (ArithmeticException)");
}
finally
{
System.out.println("Bloc Finally\n");
}
System.out.println("Instructions qui suivent
le bloc finally...");
A l'affichage nous obtenons :
Une exception s'est produite ! (ArithmeticException)
Bloc Finally
Instructions qui suivent le bloc finally...
Lorsque l'exception est levée, la main passe au bloc catch
puis au bloc finally. Si le bloc try ou lui-même ne comportent
pas d'instruction "return", le programme se poursuit avec
l'instruction qui suit le bloc finally.
Attention, de même que pour le bloc catch, le bloc finally
doit correspondre à un bloc try en particulier, les
blocs try, catch et finally doivent se suivre sans code intermédiaire
entre eux.
Nous évoquions l'instruction "return", voyons ce
que son utilisation provoque si on la place à la fin du bloc
finally:
try {
...
}
catch(ArithmeticException e) {
...
}
finally {
System.out.println("Bloc Finally\n");
return;
}
System.out.println("Instructions qui suivent
le bloc catch...");
Nous obtenons une erreur de compilation :
ZeroDivide.java:25: unreachable statement
System.out.println("Instructions qui suivent le bloc finally...");
Le compilateur a détecté que le code ne passera
jamais par l'affichage "instructions qui suivent le bloc finally",
et nous le signale.
Propagation d'une exception
Que se passe t'il lorsque le bloc catch chargé de capturer
l'exception n'est pas à proximité de la méthode
à l'origine de l'erreur ?
Si nous souhaitons voir les différentes méthodes impliquées
se transmettre l'exception, il faut adopter la syntaxe suivante,
par exemple :
public static int essai1(int c, int d) throws
ArithmeticException
Signifie que la méthode essai1 est susceptible de générer
une exception de type "Arithmetic Exception". Ce type
de déclaration est obligatoire pour les classes dont la classe
de base n'est pas "RuntimeException" (cf tutoriel précédent).
Le code source suivant utilise plusieurs méthodes dans le
seul but "d'éloigner" la méthode à
l'origine de l'erreur du catch concerné, pourtant cela fonctionne
:
(ZeroDivide.java)
public class ZeroDivide
{
static public void main(String[] args)
{
int a = 6;
int b = 0;
int z = 0;
try {
System.out.println("Avant la
division...");
z = essai1(a, b);
System.out.println("Résultat
de la division : " + z);
}
catch(ArithmeticException e) {
System.out.println("Une exception
s'est produite ! (ArithmeticException)");
}
finally {
System.out.println("Bloc Finally\n");
}
System.out.println("Instructions qui suivent le bloc catch...");
}
public static int essai1(int c, int d) throws ArithmeticException
{
return essai2(c,d);
}
public static int essai2(int e, int f) throws ArithmeticException
{
return essai3(e,f);
}
public static int essai3(int g, int h) throws ArithmeticException
{
return (g/h);
}
}
C'est bien dans la méthode "essai3" que l'exception
est levée mais celle-ci est propagée de méthodes
en méthodes jusqu'à ce qu'elle parvienne au catch
chargée de la gérer.
Nous obtenons l'affichage suivant :
Avant la division...
Une exception s'est produite ! (ArithmeticException)
Bloc Finally
Pour voir ce code fonctionner correctement il suffit de modifier
la valeur de "b" par 3 par exemple. Cependant nous souhaitons
en savoir plus sur cette erreur. Il existe plusieurs méthodes
susceptibles de nous aider, en voici deux : getMessage()
et printStackTrace(). La première décrit l'exception
en cours, la seconde envoie la trace de la pile au flux de sortie
standard, nous allons le voir. Ces méthodes sont issues de
la classe Throwable, nous exploitons ici l'objet transmis au bloc
catch :
catch(ArithmeticException e)
{
System.out.println("Une exception
s'est produite ! (ArithmeticException)" + e.getMessage());
System.out.println("Affichage
de la pile :\n");
e.printStackTrace();
}
Nous obtenons à l'écran :
Avant la division...
Une exception s'est produite ! (ArithmeticException) / by zero
Affichage de la pile :
java.lang.ArithmeticException: / by zero
at ZeroDivide.essai3(ZeroDivide.java:41)
at ZeroDivide.essai2(ZeroDivide.java:36)
at ZeroDivide.essai1(ZeroDivide.java:31)
at ZeroDivide.main(ZeroDivide.java:12)
Bloc Finally
Instructions qui suivent le bloc catch...
getMessage() apporte le " / by zero" tandis que printStackTrace
facilite le travail de débogage en précisant le chemin
parcouru par l'exception.
Dans un prochain tutoriel nous verrons comment lever nos propres
exceptions.
|