Exception

  ^ Langage
Home  |  Contact

Besoin

Fournir une gestion robuste et objet des erreurs.

Analyse

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 :

Conception

Une exception est un objet caractérisé par une classe.

Les exceptions peuvent être catégorisées en fonction de leur :

Implémentation

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 :

Exemples

Des 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 class ProblemeFichierException
extends java.lang.Exception {
   public ProblemeFichierException (String someMessage) {
      super (someMessage);
   }
}

Un exemple code Java pouvant lever une exception est :

public class FileLoader {
   public static load (String someFileName) throws ProblemeFichierException {
      if (problemeFichier)
         throw new
ProblemeFichierException ("Informations sur le problème");
   }
}

Un exemple code Java gérant des exceptions est :

public class Main {
   public static void main (String[] args)
   throws java.io.IOException {  // Peut propager cette exception
      try {
         FileLoader.load ("NomFichier.txt");
         System.out.println ("Le fichier a été ouvert");
      }
      catch (ProblemeFichierException erreurFichier) {
         System.err.println ("Le fichier n'a pu être ouvert");
      }
   }
}

Anti-exemples

Absorption

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 {
  faitQuelqueChose();
}
catch (SomeException uneExceptionFatiguanteAGerer) {
  uneExceptionFatiguanteAGerer.printStackTrace();
}

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 {
  testErreurAttendue();
  throw new TestException ("Ce test devrait produire une erreur");
}
catch (SomeException uneException) {
  // Ok, c'était attendu
}

Utilisation d'une ressource

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 :

try {
  MaResource maResource = getResource();
  try {
    maResource.utilise();
  }
  catch (MaResourceException erreurUtilisation) {
    // Gère l'erreur d'utilisation
  }
  finally {
    maResource.release();  // Or close, etc.
  }
}
catch (MaResourceException erreurAllocation) {
  // Gère l'erreur d'allocation
}

Mauvaise portée

Une erreur courante est de ne gérer les exception 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 :

try {
  SomeResult resultat = effectuePremiereTache();
  effectueSecondeTache (resultat);
}
catch (SomeExceptionType unePremiereErreur) {
  // Gère le cas d'erreur
}
catch (AnotherExceptionType uneSecondeErreur) {
  // Gère le cas d'erreur
}

Perte d'information

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
}

A 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 votre propres classes d'exceptions, ou au pire fournissez un message d'erreur détaillé :

try {
  SomeResult result = doFirstTask(parameter);
  doSecondTask (result);
}
catch (SomeException someException) {
  throw new AnotherException (someException);
}

Ou :

try {
  SomeResult result = doFirstTask(parameter);
  doSecondTask (result);
}
catch (SomeException someException) {
  throw new AnotherException (parameter, result);
}

Ou :

try {
  SomeResult result = doFirstTask(parameter);
  doSecondTask (result);
}
catch (SomeException someException) {
  throw new AnotherException (someException.getClass().getName() + ": " + someException.getMessage());
}

Traiter les exceptions comme des branches d'algorithmes

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) {
   if (someParam == null) {
     throw new MyException ("Parameter " + someParam + " cannot be null");
   }

   return doTask(someParam);
}

Notes

Home  |  Contact