Programmation Java/Exceptions
Un livre de Wikibooks.
| Programmation |
| Java |
| Sommaire |
| IHM avec SWING |
| Annexes |
| Modifier ce modèle |
Une exception est un signal qui se déclenche en cas de problème. Les exceptions permettent de gérer les cas d'erreur et de rétablir une situation stable (ce qui veut dire, dans certains cas, quitter l'application proprement). La gestion des exceptions se décompose en deux phases :
- La levée d'exceptions
- Le traitement d'exceptions.
En Java, une exception est représentée par une classe. Toutes les exceptions dérivent de la classe Exception qui dérive de la classe Throwable.
Sections |
[modifier] Levée d'exception
Une exception est levée grâce à l'instruction throw :
if (k<0) throw new EstNegatifException("Message");
Une exception peut être traitée directement par la méthode dans laquelle elle est levée, mais elle peut également être envoyée à la méthode appelante grâce à l'instruction throws (à ne pas confondre avec throw) :
public void maMethode(int entier) throws IOException { //code de la methode }
Dans cet exemple, si une exception de type IOException est levée durant l'exécution de maMethode, l'exception sera envoyée à la méthode appellant maMethode, qui devra la traiter.
Certaines exceptions sont levées implicitement par la machine virtuelle :
ArrayIndexOutOfBoundsExceptionquand l'indice d'un tableau dépasse sa capacité,ArithmeticExceptionquand une division par zéro a lieu.
Celles-ci n'ont pas besoin d'être déclarées avec l'instruction throws car elles dérivent de la classe RuntimeException, une classe d'exceptions qui ne sont pas censées être lancées par une méthode codée et utilisée correctement.
[modifier] Traitement d'exception
Le traitement des exceptions se fait à l'aide de la séquence d'instructions try...catch...finally.
- L'instruction try indique qu'une instruction (ou plus généralement un bloc d'instructions) susceptible de lever des exceptions débute.
- L'instruction catch indique le traitement pour un type particulier d'exceptions.
- L'instruction finally, qui est optionnelle, sert à définir un bloc de code à exécuter dans tous les cas, exception levée ou non.
Exemple :
public String lire(String nomDeFichier) throws IOException { try { // La ligne suivante est susceptible de lever une exception // de type FileNoFoundException FileReader lecteur = new FileReader(nomDeFichier); char[] buf = new char[100]; // Cette ligne est susceptible de lever une exception // de type IOException lecteur.read(buf,0,100); return new String(buf); } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); // Indique l'exception sur le flux d'erreur standard } finally { System.err.println("Fin de méthode"); } }
Le bloc catch (FileNotFoundException fnfe) capture toute exception du type FileNotFoundException (cette classe dérive de la classe IOException).
Le bloc finally est exécuté quelque soit ce qui se passe (exception ou non).
Toute autre exception non capturée (telle IOException) est transmise à la méthode appelante, et doit toujours être déclarée pour la méthode, en utilisant le mot clé throws, sauf les exceptions dérivant de la classe RuntimeException. S'il n'y avait pas cette exception à la règle, il faudrait déclarer throws ArrayIndexOutOfBoundsException chaque fois qu'une méthode utilise un tableau, ou throws ArithmeticException chaque fois qu'une expression est utilisée, par exemple.
[modifier] Classes et sous-classes d'exception
L'héritage entre les classes d'exceptions peut conduire à des erreurs de programmation. En effet, une instance d'une sous-classe est également considérée comme une instance de la classe de base.
[modifier] Ordre des blocs catch
L'ordre des blocs catch est important : il faut placer les sous-classes avant leur classe de base. Dans le cas contraire le compilateur gènère l'erreur exception classe_exception has already been caught.
Exemple d'ordre incorrect :
try{ FileReader lecteur = new FileReader(nomDeFichier); } catch(IOException ioex) // capture IOException et ses sous-classes { System.err.println("IOException catched :"); ioex.printStackTrace(); } catch(FileNotFoundException fnfex) // <-- erreur ici // FileNotFoundException déjà capturé par catch(IOException ioex) { System.err.println("FileNotFoundException catched :"); fnfex.printStackTrace(); }
L'ordre correct est le suivant :
try{ FileReader lecteur = new FileReader(nomDeFichier); } catch(FileNotFoundException fnfex) { System.err.println("FileNotFoundException catched :"); fnfex.printStackTrace(); } catch(IOException ioex) // capture IOException et ses autres sous-classes { System.err.println("IOException catched :"); ioex.printStackTrace(); }
[modifier] Sous-classes et clause throws
Une autre source de problèmes avec les sous-classes d'exception est la clause throws. Ce problème n'est pas détecté à la compilation.
Exemple :
public String lire(String nomDeFichier) throws FileNotFoundException { try { FileReader lecteur = new FileReader(nomDeFichier); char[] buf = new char[100]; lecteur.read(buf,0,100); return new String(buf); } catch (IOException ioe) // capture IOException et ses sous-classes { ioe.printStackTrace(); } }
Cette méthode ne lancera jamais d'exception de type FileNotFoundException car cette sous-classe de IOException est déjà capturée.
[modifier] Relancer une exception
Une exception peut être partiellement traitée, puis relancée. On peut aussi relancer une exception d'un autre type, cette dernière ayant l'exception originale comme cause.
Dans le cas où l'exception est partiellement traitée avant propagation, la relancer consiste simplement à utiliser l'instruction throw avec l'objet exception que l'on a capturé.
Exemple:
public String lire(String nomDeFichier) throws IOException { try { FileReader lecteur = new FileReader(nomDeFichier); char[] buf = new char[100]; lecteur.read(buf,0,100); return new String(buf); } catch (IOException ioException) // capture IOException et ses sous-classes { // ... traitement partiel de l'exception ... throw ioException; //<-- relance l'exception } }
Une exception d'un autre type peut être levée, par exemple pour ne pas propager une exception de type SQLException à la couche métier, tout en continuant à arrêter l'exécution normale du programme :
...
catch (SQLException sqlException) // capture SQLException et ses sous-classes
{
throw new RuntimeException("Erreur (base de données)...", sqlException);
}
...
[modifier] Catégorie d'objet lancé
Le chapitre traite des exceptions, mais en fait tout objet dont la classe est ou dérive de la classe Throwable peut être utilisé avec les mots-clés throw, throws et catch.
[modifier] Classes dérivées de Throwable
Il existe deux principales sous-classes de la classe Throwable :
Exceptionsignale une erreur dans l'application,Errorsignale une erreur plus grave, souvent au niveau de la machine virtuelle (manque de ressource, mauvais format de classe, ...).
[modifier] Créer une classe d'exception
Il est également possible d'étendre une classe d'exception pour spécialiser un type d'erreur, ajouter une information dans l'objet exception, ...
Exemple :
public class HttpException extends Exception { private int code; public HttpException(int code,String message) { super(""+code+" "+message); this.code=code; } public int getHttpCode() {return code;} }
Une instance de cette classe peut ensuite être lancée de la manière suivante :
public void download(URL url) throws HttpException { ... throw new HttpException ( 404, "File not found" ); }
et capturée comme suit :
try { download( ... ); } catch(HttpException http_ex) { System.err.println("Erreur "+http_ex.getHttpCode()); }