Page précédenteIndexPage suivante

CHAPITRE 5- Les exceptions

L'un des éléments donnant sa puissance et sa robustesse au langage Java est sa gestion avancée des exceptions. Une application gérant correctement ses exceptions doit alors pouvoir réagir à toutes les situations y compris les cas imprévus par le développeur. Dans ce sens, une erreur d'exécution peut toujours être rattrapée et le programme pourra continuer sans sortie brusque ou 'core dump'. Dans la pratique, on utilise la gestion des exceptions dans deux cas :

5.1- Lever une exception

Définitions

Il existe toutes sortes d'exceptions dont certaines font partie de l'API Java standard comme les ArithmeticException . D'autres comme une hypothétique 'VoitureSansPlaqueException' sont développées spécifiquement pour une application donnée.

Exemple d'exception:

class VoitureSansPlaqueException extends Exception{
   public String toString(){
        return "Cette voiture ne possède pas de plaque d'immatriculation";
  }
}

Nous verrons plus loin à quoi sert la méthode toString(). Il est important de noter que dans la majorité des cas, vous n'aurez pas à implémenter de nouvelles exceptions mais simplement à utiliser celles déjà existantes. Les exceptions personnelles servent à créer des types d'erreur spécifiques qui permettront une granulosité plus fine à la gestion des événements. Dans le cas ou on ne cherche pas à déterminer la cause précise de l'erreur mais simplement à détecter un problème quelconque  pour afficher un message générique, nous pouvons directement utiliser les objets de type Exception .

Les exceptions sont dites levées ou envoyées par le mot-clé throw.

Le mot clé throw sert à initier le trajet de l'exception. Au rugby, ce serait le premier lancé du ballon.

Exemple:

La méthode getPlaque prend en argument un objet Voiture et renvoie un objet PlaqueImmatriculation. Si la voiture ne possède pas de plaque, le programme considère qu'une erreur applicative est commise et lève alors une VoitureSansPlaqueException:

public PlaqueImmatriculation getPlaque(Voiture v) throws VoitureSansPlaqueException {
    if (v.plaque==null){
        throw new VoitureSansPlaqueException();
    }
    else{
        return v.plaque;
    }
}

Le mot-clé new instancie l'exception et le mot-clé throw lance l'exception qui va alors être traitée ou propagée.


Remarque : Attention à ne pas confondre le mot clé throw qui sert à lever une exception avec le mot clé throws qui sert à propager l'exception.


5.2- Capturer ou propager

Lorsqu'une exception est levée,il y a deux possibilités pour le programme:

Détaillons ces deux processus:

Propagation de l'exception

Considérons l'exemple de la méthode getPlaque :

public PlaqueImmatriculation getPlaque(Voiture v) throws VoitureSansPlaqueException {

    if (v.plaque==null){
        throw new VoitureSansPlaqueException();
    }
    else{
        return v.plaque;
    }
}

Nous voyons que dans certains cas, cette méthode peut lever une exception. Cependant, elle ne la traite pas. Elle n'affiche pas de message d'erreur et n'effectue aucun traitement particulier. Elle se contente de propager l'exception à la méthode appelante par le mot-clé  throws.

Dans la déclaration de la méthode, " throws VoitureSansPlaqueException" signifie que la méthode est susceptible de propager l'exception VoitureSansPlaqueException.

Dans ce cas, ce sera à la méthode appelante de traiter l'exception ou alors de la propager à son tour. La propagation d'une exception de méthodes en méthodes peut être affichées par la méthode printStackTrace() de l'exception.

Traitement de l'exception

L'exception sera propagée de méthodes en méthodes tant qu'elle ne sera pas traitée.
Pour traiter une exception, il suffit d'utiliser les mots-clés try et catch:

try{
    getPlaque(v1);
    afficherPlaque();
}
catch(VoitureSansPlaqueException vspe){
    vspe.printStackTrace();
    afficherMessageErreur();
}

Le mot-clé try précise que les instructions contenues dans ces accolades peuvent lever des exceptions dont le type sera précisé en argument du catch associé.

Si la voiture v1 ne possède pas de fournisseur, la méthode getPlaque() - comme nous l'avons vu- lèvera une exception de type  VoitureSansPlaqueException et la propagera. A la ligne getPlaque(v1), l'exception est alors capturée. La ligne afficherPlaque() n'est pas exécutée et le programme passe alors aux instructions du catch correspondant.

Résumons ce mécanisme

Lorsqu'une exception est levée, le programme arrête son exécution et ne reprendra que dans un catch() adéquat de l'exception. La méthode qui a levé l'exception va alors la traiter immédiatement ou va la propager à la méthode appelante qui à son tour va traiter l'exception ou la propager. La seule façon de stopper ce parcours sera de traiter l'exception ( catch ). Il faut voir ce phénomène comme des passes au rugby: l'exception est le ballon et il est transmis de joueurs en joueurs jusqu'à celui qui va 'traiter l'exception' et marquer l'essai.

 

5.3- Compléments sur les exceptions

Maintenant que nous maîtrisons les mécanismes fondamentaux des exceptions, nous allons détailler certaines méthodes de programmation.

Les méthode toString() et printStackTrace() de la classe Exception

Cette méthode renvoie la chaîne de caractère décrivant l'erreur. Il est souvent judicieux de surcharger cette méthode à l'implémentation d'une classe fille de Exception. La classe Exception quant à elle ne contient pas de message par défaut, il peut être utile de construire un objet Exception avec un String décrivant le problème. Exemple:

public void test() throws Exception{
    if ( - pbm de division par zéro -){
        throw new Exception("Division par Zéro");
    }
    if (- pbm de nombre trop petit -){
        throw new Exception("Nombre trop petit");
    }
}

L'instruction

try{
    test();
}
catch(Exception e){
    System.out.println(e);
}

Affichera "Division par zéro" ou "Nombre trop petit" selon le cas.

Mieux encore: L'instruction e.printStackTrace() ou 'e' est l'exception affichera toujours les messages d'erreurs mais donnera le parcours complet de l'exception du moment où elle a été levée jusqu'à celui où elle a été capturée. Cette méthode de la classe Exception est très utile au debugage.

Capture sélective

Il est possible de réaliser des filtres d'exceptions dans des cas complexes. Par exemple, admettons que la méthode getPlaque vue précédemment puisse lever une VoitureSansPlaqueException mais également tout autre type d'exception. Dans ce cas, le code sera le suivant:

try{
    getPlaque(v1);
}
catch( VoitureSansPlaqueException vspe){
    vspe.printStackTrace();
}
catch( Exception e){
    e.printStackTrace();
}

Nous effectuons ainsi une capture sélective avec granulosité décroissante. Bien entendu, aucune exception ne pourra passer outre un catch(Exception) puisque toutes les exceptions dérivent de la classe Exception.
 

Si nous voulons réaliser une action qu'il y ait une exception ou non, il faut utiliser le mot-clé finally

try{
    getPlaque(v1);
}
catch( VoitureSansPlaqueException vspe){
    vspe.printStackTrace();
}
catch( Exception e){
    e.printStackTrace();
}
finally{
   System.out.println("Traitement terminé");  //ce code est exécuté qu'il y ait exception ou non
}

Remarque sur les déclarations de variables

Comme pour les boucles itératives, les variables locales définies dans un try ou un catch ne sont connues que dans ces zones.



TD5 - Les exceptions

Exercice 1- Division par zéro

1- Écrire un programme qui effectue une division par zéro et ne contient aucun traitement d'exception. Que se passe-t-il? Pourquoi? Quel est le type de l'exception générée.

2- Cette fois, ré-écrire le programme pour capturer l'exception.

3- Le modifier pour afficher un message d'erreur explicite.

4- Cette fois, le programme corrige lui même et remplace la division par zéro par une division par 1.

Remarquons ainsi que lever une exception ne signifie forcement l'affichage d'un message d'erreur suivi de l'arrêt du programme mais qu'il peut y avoir poursuite normale de l'exécution.

Exercice 2- L'âge du capitaine

1- Écrire une méthode getAgeCap() qui demande l'âge du capitaine. Cet âge doit être compris entre 18 et 65 ans et doit être un entier sous peine de lever une AgeCapException. Vous implementerez cette exception pour qu'elle renvoie une description explicite du type "[proposition] ans n'est pas un âge valide". Le programme devra également être en mesure de capturer tout type d'exception autre que AgeCapException.

2- Dans un premier temps, la méthode getAgeCap() propagera l'exception à la méthode appelante qui la traitera.

3- Modifier le programme pour que ce soit la méthode getAgeCap() qui traite l'exception.

4- Modifier encore le programme pour que getAgeCap() traite l'exception mais lève une seconde exception de type Exception pour signaler à la méthode appelante qu'une erreur s'est produite et que cette dernière comptabilise le nombre d'essais infructueux et l'affiche. Le programme demandera l'âge du capitaine en boucle infinie.

Exercice 3- Saisie d'un mot de passe

Dans les failles de sécurité réseau, on trouve très souvent les problèmes de dépassement. Par exemple, sur certaines anciennes versions de telnet, un login ou un mot de passe de plus d'un mega-octet faisait "planter" le programme et on obtenait alors un accès root au système. Ce programme va gérer ce type de problème en séparant les exceptions pour une meilleure gestion.

1- Écrire un programme stand-alone qui demande en boucle un nom d'utilisateur (login) et un mot de passe (pwd) jusqu'à recevoir un login/pwd correct.

Le seul utilisateur référencé sera scott / tiger ( à mettre en constante dans la classe principale ).

2- Implémenter les exceptions suivantes:

* WrongLoginException qui se produit lorsque l'utilisateur saisit un login inexistant

* WrongPwdException lorsque le mot de passe est erroné

* WrongInputLength lorsque le login où le pwd saisi dépasse 10 caractères.

3- Implémenter de façon à utiliser ces exceptions de façon judicieuse et avec la granulosité la plus fine.

Remarque: La lecture de l'entrée standard peut lever une java.io.IOException...


solutions



Page précédenteIndexPage suivante