Fournir dans un langage une gestion robuste et objet des erreurs .
Les exceptions représentent des erreurs. Tout code pouvant signaler des erreurs est susceptible de "lever" ou lancer (throw) des exceptions.
Lorsqu'un code appele un autre code pouvant lever des exceptions, la compilation lui impose de choisir entre :
Une exception est un objet caractérisé par une classe.
Les exceptions peuvent être catégorisées en fonction de leur :
Toutes les exceptions implémentent l'interface java.lang.Throwable
, ce qui permet de les lancer (throw).
Elles le font généralement en dérivant de :
java.lang.Exception
pour les exceptions contrôléesjava.lang.RuntimeException
pour les exceptions non-contrôléesDes exemples d'exceptions sont :
Exemples de types d'exceptions | Contrôlées | Non-contrôlées |
---|---|---|
Sans exception(s) imbriquée(s) | Exception, IOException |
RuntimeException, ArrayOutOfBoundsException, NullPointerException |
Avec exception(s) imbriquée(s) | SQLException |
JDOException |
Un exemple de classe d'exception est :
public
<strong>class</strong> ProblemeFichierException<br> <strong>extends java.lang.Exception</strong> {<br> <strong>public </strong>ProblemeFichierException (String someMessage) {<br> <strong>super </strong>(someMessage);<br> } <br> }
Un exemple code Java pouvant lever une exception est :
public
<strong>class</strong> FileLoader {<br> <strong>public static </strong>load (String someFileName) <strong>throws </strong>ProblemeFichierException {<br> <strong>if (</strong>problemeFichier<strong>)<br> throw new </strong>ProblemeFichierException <strong>(<span class="codeString">"</span></strong><span class="codeString">Informations sur le problème<strong>"</strong></span><strong>);</strong><br> } <br> }
Un exemple code Java gérant des exceptions est :
public
<strong>class</strong> Main {<br> <strong>public </strong>static void main (String[] args)<br> <strong>throws </strong>java.io.IOException { <span class="codeComment">// Peut propager cette exception</span><br> <strong>try</strong> { <br> FileLoader.load<strong> (<span class="codeString">"</span></strong><span class="codeString">NomFichier.txt<strong>"</strong></span><strong>);</strong><br> <strong>System.out.println </strong>(<span class="codeString">"Le fichier a été ouvert"</span>);<br> }<br> <strong>catch </strong>(ProblemeFichierException erreurFichier) {<br> <strong>System.err.println </strong>(<span class="codeString">"Le fichier n'a pu être ouvert"</span>);<br> } <br> } <br> }
N'éludez jamais le traitement d'une exception contrôlée. Cela pourra donner l'illusion que votre code tourne, alors que ce n'est pas le cas (erreurs difficiles à trouver) :
try {
faitQuelqueChose();
}
catch (SomeException uneExceptionFatiguanteAGerer) {
}
La gestion minimale d'une exception devrait être :
try {<br> faitQuelqueChose();<br> }<br> catch (SomeException uneExceptionFatiguanteAGerer) {<br> <strong>uneExceptionFatiguanteAGerer.printStackTrace</strong>();<br> }
Dans de rares cas, il pourra être admissible d'asborber une exception. Cependant, même dans ce cas le lecteur doit savoir que c'est normal et pourquoi c'est normal :
try {<br> testErreurAttendue();<br> throw new TestException (<span class="codeString">"Ce test devrait produire une erreur"</span>);<br> }<br> catch (SomeException uneException) {<br> <span class="codeComment"><strong>// Ok, c'était attendu</strong></span><br> }
Vouloir éviter les blocs de gestion d'erreur peut rendre votre code moins robuste et moins clair. Voici un exemple de mauvaise gestion des erreurs lors de l'utilisation d'une ressource (connexion, fichier, etc.):
MaResource maResource = null;
try {
MaResource maResource = getResource();
maResource.utilise();
}
catch (MaResourceException uneErreur) {
// Gère l'erreur
}
finally {
if (maResource != null)
maResource.release();
}
Voici une version claire, qui sait différencier les causes des erreurs, et qui n'a pas besoin de faire des tests inutiles :
<strong>try</strong> { <br> MaResource maResource = getResource();<br> <strong> try</strong> {<br> maResource.utilise();<br> }<br> <strong> catch</strong> (MaResourceException erreurUtilisation) {<br> <span class="codeComment">// Gère l'erreur d'utilisation</span><br> }<br> <strong> finally</strong> {<br> maResource.release(); // Or close, etc.<br> }<br> } <br> <strong>catch</strong> (MaResourceException erreurAllocation) {<br> <span class="codeComment">// Gère l'erreur d'allocation</span><br> }
Une erreur courante est de ne gérer les exceptions qu'au niveau d'une instruction ou au niveau d'un ensemble réduit d'instructions qui pourtant dépendent de la réussite d'instructions précédentes :
SomeResult resultat;
try {
resultat = effectuePremiereTache();
}
catch (SomeExceptionType unePremiereErreur) {
// Gère le cas d'erreur
}
try {
effectueSecondeTache(resultat); // Quid si resultat n'a pas pu s'initialiser ?
}
catch (AnotherExceptionType uneSecondeErreur) {
// Gère le cas d'erreur
}
Une version correcte inclue dans une bloc try
un ensemble d'actions dépendantes :
<strong>try</strong> {<br> SomeResult resultat = effectuePremiereTache();<br> effectueSecondeTache (resultat);<br> }<br> <strong>catch</strong> (SomeExceptionType unePremiereErreur) {<br> <span class="codeComment">// Gère le cas d'erreur</span><br> }<br> <strong>catch</strong> (AnotherExceptionType uneSecondeErreur) {<br> <span class="codeComment">// Gère le cas d'erreur</span><br> }
L'information étant critique pour comprendre et éventuellement corriger une erreur, ne pas remonter toutes les informations instructives peut être très dommageable pour l'utilisateur ou le code appelant.
Il faut donc éviter du code du genre :
try {
SomeResult result = doFirstTask(parameter);
doSecondTask (result);
}
catch (SomeException someException) {
System.err.println ("Il y a eu un problème"); // Lequel ? Pourquoi ?
}
Ou encore :
try {
SomeResult result = doFirstTask(parameter);
doSecondTask (result);
}
catch (SomeException someException) {
throw new AnotherException(); // Perd l'info de l'erreur initiale
}
À la place, utilisez les exceptions imbriquées (il en existe dans certaines exceptions standards ou dans toutes depuis J2SE 1.4), ou ajouter des champs d'info à remplir dans vos propres classes d'exceptions, ou au pire, fournissez un message d'erreur détaillé :
try {<br> SomeResult result = doFirstTask(parameter);<br> doSecondTask (result);<br> }<br> catch (SomeException someException) {<br> throw <strong>new AnotherException (someException)</strong>;<br> }
Ou :
try {<br> SomeResult result = doFirstTask(parameter);<br> doSecondTask (result);<br> }<br> catch (SomeException someException) {<br> throw <strong>new AnotherException (parameter, result)</strong>;<br> }
Ou :
try {<br> SomeResult result = doFirstTask(parameter);<br> doSecondTask (result);<br> }<br> catch (SomeException someException) {<br> throw <strong>new AnotherException (someException.getClass().getName() + ": " + someException.getMessage(</strong>));<br> }
Une exception ne doit pas être considérée comme un cas de traitement normal :
public Result method (Object someParam) {
if (someParam != null) {
return doTask(someParam);
}
else {
throw new MyException ("Parameter " + someParam + " cannot be null");
}
}
Une exception plutôt, comme son nom l'indique, prévenir les cas exceptionnels et être gérées comme des pré/postconditions de traitements :
public Result method (Object someParam) {<br> <strong> if (someParam == null) {<br> throw new MyException ("Parameter " + someParam + " cannot be null");<br> }<br> </strong><br> return doTask(someParam);<br> <strong></strong>}
java.io.Serializable
pour être propagées entre objets distants
(via le réseau).