3.2. Traiter des exceptions pour améliorer
la robustesse



Plan de ce chapitre:   ...........retour au plan général

1. Les exceptions en Java : syntaxe, rôle, classes

    1.1 Définitions et rappels
    1.2 Comment gérer une exception dans un programme

    1.3 Interception d'exceptions hiérarchisées
    1.4 Ordre d'interception d'exceptions hiérarchisées
    1.5 Redéclenchement d'une exception : thtrow

    1.6 Exception vérifiée ou non
    1.7 Méthode propageant une exception vérifiée : throws
1.8 Clause finally

2. Un exemple complet et détaillé de  traitement

    2.1 Enoncé
    2.2 La démarche
    2.3 Un programme Java solution


1. Les exceptions en Java : syntaxe, rôle, classes


1.1 Définition et rappels

Rappellons au lecteur que nous avons déjà vu les base sur les exceptions avec Delphi au chapitre 5.9 du package, la sécurité d'une application peut être rendue instable par toute une série de facteurs :

  • Des problèmes liés au matériel : par exemple la perte subite d'une connection à un port, un disque défectueux...
  • Des actions imprévues de l'utilisateur, entrainant par exemple une division par zéro...
  • Des débordements de stockage dans les structures de données...

  • Toutefois les faiblesses dans un logiciel pendant son exécution, peuvent survenir : lors des entrées-sorties, lors de calculs mathématiques interdits (comme la division par zéro), lors de fausses manoeuvres de la part de l’utilisateur, ou encore lorsque la connexion à un périphérique est inopinément interrompue, lors d'actions sur les données. Le logiciel doit donc se " défendre " contre de tels incidents potentiels, nous nommerons cette démarche la programmation défensive !

    Progammation défensive
    La programmation défensive est une attitude de pensée consistant à prévoir que le logiciel sera soumis à des défaillances dues à certains paramètres externes ou internes et donc à prévoir une réponse adaptée à chaque type de situation.

    En programmation défensive il est possible de protéger directement le code à l’aide de la notion d’exception. L’objectif principal est d’améliorer la qualité de " robustesse " (définie par B.Meyer) d’un logiciel. L’utilisation des exceptions avec leur mécanisme intégré, autorise la construction rapide et efficace de logiciels robustes.

    Rôle d’une exception
    Une exception est chargée de signaler un comportement exceptionnel (mais prévu) d’une partie spécifique d’un logiciel. Dans les langages de programmation actuels, les exceptions font partie du langage lui-même. C’est le cas de Java qui intègre les exceptions comme une classe particulière : la classe Exception. Cette classe contient un nombre important de classes dérivées.


    Comment agit une exception

    Dès qu’une erreur se produit comme  un manque de mémoire , un calcul impossible, un fichier inexistant, un transtypage non valide,..., un objet de la classe adéquate dérivée de la classe Exception est instancié. Nous dirons que le logiciel " déclenche une exception ".



    1.2 Comment gérer une exception dans un programme

    Programme sans gestion de l'exception
    Soit un programme Java contenant un incident d'exécution (une division par zéro dans l'instruction x = 1/0; ) dans la méthode meth() de la classe Action1, cette méthode est appelée dans la classe UseAction1 à travers un objet de classe Action1 :



    Lors de l'exécution, 
    après avoir affiché les chaînes "Début du programme" et "  ...Avant incident", le programme s'arrête et la java machine signale une erreur. Voici ci-dessous l'affichage obtenu sur la console lors de l'exécution :

    ---- java UseAction1
    Début du programme
      ...Avant incident

    java.lang.ArithmeticException : / by zero
    ---- : Exception in thread "main"


    Que s'est-il passé ?

    La méthode main :
     - a instancié un objet Obj de classe Action1,;
     - a affiché sur la console la phrase "Début du programme",
     - a invoqué la méthode meth() de l'objet Obj,
     - a affiché sur la console la phrase "   ...Avant incident",
     - a exécuté l'instruction "x = 1/0;"

    Dès que l'instruction
    "x = 1/0;" a été exécutée celle-ci a provoqué un incident. En fait une exception de la classe 
    ArithmeticException a été "levée" (un objet de cette classe a été instancié) par la Java machine.

    La Java machine a arrêté le programme immédiatement à cet endroit parce qu'elle n'a pas trouvé de code d'interception de cette exception qui a été automatiquement levée :



    Nous allons voir comment intercepter (on dit aussi "attraper" - to catch) cette exception afin de faire réagir notre programme afin qu'il ne s'arrête pas brutalement.



    Programme avec gestion de l'exception

    Java possède une instruction de gestion des exceptions,
    qui permet d'intercepter des exceptions dérivant de la classe Exception :

    try ... catch

    Syntaxe minimale d'un tel gestionnaire
     :
    try {
      <lignes de code à protéger>
    }catch ( UneException E ) {
      <lignes de code réagissant à l’exception UneException >
    }

    Le type UneException est obligatoirement une classe qui hérite de la classe Exception.



    Schéma du fonctionnement d'un tel gestionnaire :


    Le gestionnaire d'exception "déroute" l'exécution du programme vers le bloc d'interception catch qui traite l'exception (exécute le code contenu dans le bloc catch), puis renvoie et continue l'exécution du programme  vers le code situé après le gestionnaire lui-même.



     

    Principe de fonctionnement de l'interception

    Dès qu'une exception est levée (instanciée), la Java machine stoppe immédiatement l'exécution normale du programme à la recherche d'un gestionnaire d'exception susceptible d'intercepter (saisir) et traiter cette exception. Cette recherche s'effectue  à partir du bloc englobant et se poursuit sur les blocs plus englobants si aucun gestionnaire de cette exception n'a été trouvé.

    Soit le même programme Java que précédemment, contenant un incident d'exécution (une division par zéro dans l'instruction x = 1/0; ). Cette fois nous allons gérer l'incident grâce à un gestionnaire d'exception try..catch dans le bloc englobant immédiatement supérieur.


    Programme sans traitement de l'incident :


    Programme avec traitement de l'incident par try...catch :


    Ci-dessous l'affichage obtenu sur la console lors de l'exécution de ce programme :


    ---- java UseAction1
    Début du programme.
      ...Avant incident
    Interception exception
    Fin du programme.

     ---- : operation complete.





    1.3 Interception d'exceptions hiérarchisées

    Interceptions de plusieurs exceptions


    Dans un gestionnaire try...catch, il est en fait possible d'intercepter plusieurs types d'exceptions différentes et de les traiter.

    Ci-après nous montrons la syntaxe d'un tel gestionnaire qui fonctionne comme un selecteur ordonné, ce qui signifie qu'une seule clause d'interception est exécutée
    .

    Dès qu'une exception intervient dans le
    < bloc de code à protéger>, la Java machine scrute séquentiellement toutes les clauses catch de la première jusqu'à la nième. Si l'exception actuellement levée est d'un des types présents dans la liste des clauses le traitement associé est effectué,la scrutation est abandonnée et le programme poursuit son exécution après le gestionnaire.

    Syntaxe :
    try {
      < bloc de code à protéger>
    }
    catch
    ( TypeException1 E ) { <Traitement TypeException1 > }
    catch ( TypeException2 E ) { <Traitement TypeException2 > }
    .....
    catch ( TypeExceptionk E ) { <Traitement TypeExceptionk > }

    Où TypeException1, TypeException12, ... , TypeExceptionk sont des classes d'exceptions obligatoirement toutes distinctes.

    Seule une seule clause catch ( TypeException E ) {...}est exécutée (celle qui correspond au bon type de l'objet d'exception instancié).

    Exemple théorique, supposons que la méthode meth() de la classe Action2 puisse lever trois types différents d'exceptions: ArithmeticException, ArrayStoreException, ClassCastException.

    Notre gestionnaire d'exceptions est programmé pour intercepter l'une de ces 3 catégories. Nous figurons ci-dessous les trois schémas d'exécution correspondant chacun à la levée (l'instanciation d'un objet) d'une exception de l'un des trois types et son interception :


    Interception d'une
    ArrayStoreException :



    Interception d'une ClassCastException :



    Interception d'une ArithmeticException :




    1.4 Ordre d'interception d'exceptions hiérarchisées


    Dans un gestionnaire try...catch comprenant plusieurs clauses, la recherche de la  clause catch contenant le traitement de la classe d'exception appropriée, s’effectue séquentiellement dans l’ordre d’écriture des lignes de code.

    Soit le pseudo-code java suivant :
    try {
      < bloc de code à protéger générant un objet exception>
    }
    catch
    ( TypeException1 E ) { <Traitement TypeException1 > }
    catch ( TypeException2 E ) { <Traitement TypeException2 > }
    .....
    catch ( TypeExceptionk E ) { <Traitement TypeExceptionk > }

    La recherche va s'effectuer comme si le programme contenait des if...else if... imbriqués. Les tests seraient effectués sur l'appartenance de l'objet d'exception à une classe à l'aide de l'opérateur instanceof :

    if (<Objet exception> instanceof TypeException1)  { <Traitement TypeException1 > }
    else if (<Objet exception> instanceof TypeException2)  { <Traitement TypeException2 > }
    ...
    else if (<Objet exception> instanceof TypeExceptionk)  { <Traitement TypeExceptionk > }

    Rappellons que l'opérateur instanceof agit sur une classe et ses classes filles (sur une hérarchie de classes), c'est à dire que tout objet de classe TypeExceptionX est aussi considéré comme un objet de classe parent au sens du test d'appartenance en particulier cet objet de classe TypeExceptionX est aussi considéré objet de classe Exception qui est la classe mère de toutes les exceptions Java.
     
    Le test d'appartenance de classe dans la recherche d'une clause catch fonctionne d'une façon identique à l'opérateur instanceof dans les if...else

    On choisira donc, lorsqu’il y a une hiérarchie entre les exceptions à intercepter, de placer le code de leurs gestionnaires dans l’ordre inverse de la hiérarchie.

    Exemple : Soit une hiérarchie d'exceptions dans java

    java.lang.Exception
      |
      +--java.lang.RuntimeException
              |
             +--java.lang.ArithmeticException
              |
             +--java.lang.ArrayStoreException
              |
             +--java.lang.ClassCastException

    Soit le modèle de gestionnaire d'interception déjà fourni plus haut :

    try {
      < bloc de code à protéger générant un objet exception>
    }
    catch
    ( ArithmeticException E ) { <Traitement ArithmeticException> }
    catch ( ArrayStoreException E ) { <Traitement ArrayStoreException> }
    catch ( ClassCastException E ) { <Traitement ClassCastException> }

    Supposons que nous souhaitions intercepter une quatrième classe d'exception, par exemple une RuntimeException, nous devons rajouter une clause :

     
    catch ( RuntimeException E ) { <Traitement RuntimeException>}

    Insérons cette clause en premier dans la liste des clauses d'interception :



    Nous lançons ensuite la compilation de cette classe et nous obtenons un message d'erreur :



    Résultats de l'exécution :
     ---- java UseAction2
     
    UseAction2.java:19: exception java.lang.ArithmeticException has already been caught
                     catch(ArithmeticException E){
                     ^
     UseAction2.java:22: exception java.lang.ArrayStoreException has already been caught
                     catch(ArrayStoreException E){
                     ^
      UseAction2.java:25: exception java.lang.ClassCastException has already been caught
                     catch(ClassCastException E){
                     ^
        3 errors


    Le compilateur proteste à partir de la clause
    catch ( ArithmeticException E )en nous indiquant que l'exception est déjà interceptée et ceci trois fois de suite.

    Que s'est-il passé ?

    Le fait de placer en premier la clause 
    catch ( RuntimeException E ) chargée d'intercepter les  exceptions de classe RuntimeException implique que n'importe quelle exception héritant de RuntimeException comme par exemple ArithmeticExceptionest considérée comme une RuntimeException. Dans un tel cas cette ArithmeticException est interceptée par la clause  catch ( RuntimeException E ) mais elle n'est jamais interceptée par la clause catch ( ArithmeticException E ).

    Le seul endroit où le compilateur Java acceptera
    l'écriture de la clause
    catch ( RuntimeException E ) se situe à la fin de la liste des clauses catch. Ci-dessous l'écriture d'un programme correct :



    Dans ce cas la recherche séquentielle dans les clauses permettra le filtrage correct des classes filles puis ensuite 
    le filtrage des classes mères.

    On choisira donc, lorsqu’il y a une hiérarchie entre les exceptions à intercepter, de placer le code de leurs clauses dans l’ordre inverse de la hiérarchie.



    1.5 Redéclenchement d'une exception : throw

    Il est possible de déclencher soi-même des exceptions en utilisant l'instruction throw, voir même de déclencher des exceptions personnalisées ou non.

    Déclenchement manuel d'une exception existante

    La Java machine peut déclencher une exception automatiquement comme dans l'exemple de la levée d'une
    ArithmeticException lors de l'exécution de l'instruction "x = 1/0 ;".

    La Java machine peut aussi lever (déclencher) une exception à votre demande suite à la rencontre d'une instruction throw. Le programme qui suit lance une ArithmeticException avec le message "Mauvais calcul !" dans la méthode meth() et intercepte cette exception dans le bloc englobant main. Le traitement de cette exception consiste à afficher le contenu du champ message de l'exception grâce à la méthode getMessage() :



    Résultats de l'exécution :
     ---- java UseAction3
    Début du programme.
      ...Avant incident
    Interception exception : Mauvais calcul !
    Fin du programme.

    ---- : operation complete.

    Déclenchement manuel d'une exception personnalisée

    Pour une exception personnalisée, le mode d'action est strictement identique, il vous faut seulement auparavant créer une nouvelle classe héritant obligatoirement de la classe Exception ou de n'importe laquelle de ses sous-classes.

    Reprenons le programme précédent et créons une classe d'exception que nous nommerons
    ArithmeticExceptionPerso héritant de la classe des ArithmeticException puis exécutons ce programme :


    Résultats de l'exécution :
     ---- java UseAction3
    Début du programme.
      ...Avant incident
    Interception exception : Mauvais calcul !
    Fin du programme.

    ---- : operation complete.

    L'exécution de ce programme est identique à celle du programme précédent, notre exception fonctionne bien comme celle de Java.
     



    1.6 Exception vérifiée ou non

    La majorité des exceptions de Java font partie du package
    java.lang. Certaines exceptions sont tellement courantes qu'elles ont été rangées par Sun (concepteur de Java) dans une catégorie dénommée la catégorie des exceptions implicites ou non vérifiées (unchecked), les autres sont dénommées exception explicites ou vérifiées (checked) selon les auteurs.

    Une 
    exception  non vérifiée (implicite) est une classe dérivant de l'une des deux classes Error ou RuntimeException :

    java.lang.Object
      |
      +--java.lang.Throwable
            |
            +--java.lang.Error

            |
            +--java.lang.Exception
                  |
                  +--java.lang.RuntimeException

    Toutes les exceptions vérifiées ou non fonctionnent de la même manière, la différence se localise dans la syntaxe à adopter dans une méthode propageant l'un ou l'autre genre d'exception.

    Nous pouvons déjà dire que pour les exceptions non vérifiées, il n'y a aucune contrainte syntaxique pour propager une exception d'une méthode vers un futur bloc englobant. La meilleure preuve de notre affirmation est qu'en fait nous n'avons utilisé jusqu'ici que des exceptions non vérifiées, plus précisément des exceptions dérivant de
    RuntimeException et que nous n'avons utilisé aucune syntaxe spéciale dans la méthode meth() pour indiquer qu'elle était susceptible de lever une exception :

    +--java.lang.RuntimeException
             |
             +--java.lang.ArithmeticException
             |
             +--java.lang.ArrayStoreException
             |
             +--java.lang.ClassCastException

    Il n'en est pas de même lorsque l'exception lancée (levée) dans la méthode meth() est une exception vérifiée. Le paragraphe suivant vous explique comment agir dans ce cas.



    1.7 Méthode propageant une exception vérifiée : throw

    Une méthode dans laquelle est levée une ou plusieurs exceptions vérifiées doit obligatoirement signaler au compilateur quelles sont les classes d'exceptions qu'elle laisse se propager sans traitement par un gestionnaire (propagation vers un bloc englobant).

    Java dispose d'un spécificateur pour ce signalement : le mot clef throws suivi de la liste des noms des classes d'exceptions qui sont propagées.

    Signature générale d'une méthode propageant des exceptions vérifiées
    <modificateurs> <type> < identificateur>  ( <liste param formels> )
     throws
    < liste d'exceptions>
    {
    .....
    }

    Exemple :

    protected static void meth ( int x, char c )
       throws IOException, ArithmeticException
    {
      .....
    }

    Nous allons choisir une classe parmi les très nombreuses d'exceptions vérifiées, notre choix se porte sur une classe d'exception très utilisée, la classe IOException qui traite de toutes les exceptions d'entrée-sortie. Le J2SE 1.4.1 donne la liste des classes dérivant de la classe IOException   :

    ChangedCharSetException, CharacterCodingException, CharConversionException, ClosedChannelException, EOFException, FileLockInterruptionException, FileNotFoundException, IIOException, InterruptedIOException, MalformedURLException, ObjectStreamException, ProtocolException, RemoteException, SocketException, SSLException, SyncFailedException, UnknownHostException, UnknownServiceException, UnsupportedEncodingException, UTFDataFormatException, ZipException.

    Reprenons le programme écrit au paragraphe précédent concernant le déclenchement manuel d'une exception existante en l'appliquant à la classe d'exception existante IOException, cette classe étant incluse dans le package java.io et non dans le package java.lang importé implicitement, on doit ajouter une instruction d'importation du package java.io, puis exécutons le programme tel quel. Voici ce que nous obtenons :

     

    Résultats de l'exécution :

     ---- java UseAction4
     UseAction4.java:8: unreported exception java.io.IOException; must be caught or declared to be thrown
                    throw new IOException("Problème d'E/S !");
                    ^
        1 error


    Que s'est-il passé ?


    Le compilateur Java attendait de notre part l'une des deux seules attitudes suivantes :

    Soit nous interceptons et traitons l'exception
    IOException à l'intérieur du corps de la méthode où elle a été lancée avec pour conséquence que le bloc englobant n'aura pas à traiter une telle exception puisque l'objet d'exception est automatiquement détruit dès qu'il a été traité. Le code ci-après implante cette première attitude :


    Résultats de l'exécution :
     ---- java UseAction4
    Début du programme.
      ...Avant incident
    Interception exception : Problème d'E/S !
      ...Après incident
    Fin du programme.


    Soit nous interceptons et traitons l'exception IOException à l'intérieur du bloc englobant la méthode meth() qui a lancé l'exception, auquel cas il est obligatoire de signaler au compilateur que cette méthode meth() lance et propage une IOException non traitée. Le code ci-après implante la seconde attitude possible :


    Résultats de l'exécution :
     ---- java UseAction4
    Début du programme.
      ...Avant incident
    Interception exception : Problème d'E/S !
    Fin du programme.



    Redéfinition d'une méthode propageant des exceptions vérifiées

    Principe de base : la partie throws < liste d'exceptions> de la signature de la méthode qui redéfinit une méthode de la super-classe peut comporter moins de types d'exception. Elle ne peut pas propager plus de types ou des types différents de ceux de la méthode de la super-classe.


    Ci-après un programme avec une super-classe Action4 et une méthode meth(), qui est redéfinie dans une classe fille nommée Action5 :



    Voici la hiérarchie des classes utilisées :

    +--java.lang.Exception
            |
           +--java.io.IOException                   
            |   |
            |  +--java.io.CharConversionException
            |
           +--java.lang.NoSuchFieldException

    Notez que la méthode meth de la super-classe propage IOException et NoSuchFieldException bien qu'elle ne lance qu'une exception de type
    IOException; ceci permettra la redéfinition dans la classe fille.

    Voici pour terminer, un code source de classe utilisant la classe Action5 précédemment définie et engendrant aléatoirement l'un des 3 types d"exception :



    Analysez et comprenez bien le fonctionnement de ce petit programme.


    Listing des 3 exécutions dans chacun des cas d'appel de la méthode meth :

    Résultats de l'exécution

     ---- java UseAction5
    Début du programme.
    Appel meth(0)
      ...Avant incident-fille
      ...Avant incident-mère
    Interception exception_0 : Problème conversion de caractère !-mère
    Fin du programme.


    Résultats de l'exécution
     ---- java UseAction5
    Début du programme.
    Appel meth(2)
      ...Avant incident-fille
    Interception exception_2 : Problème de champ !-fille
    Fin du programme.


    Résultats de l'exécution
     ---- java UseAction5
    Début du programme.
    Appel meth(1)
      ...Avant incident-fille
    Interception exception_1 : Problème d'E/S !-fille
    Fin du programme.



    1.8 Clause finally

    Supposons que nous soyons en présence d'un code contenant une éventuelle levée d'exception, mais supposons que quoiqu'il se passe nous désirions qu'un certain type d'action ait toujours lieu (comme par exemple fermer un fichier qui a été ouvert auparavant). Il existe en Java une clause spécifique optionnelle dans la syntaxe des gestionnaires d'exception permettant ce type de réaction du programme, c'est la clause finally. Voici en pseudo Java une syntaxe de cette clause :

    <Ouverture du fichier>
    try {
    < action sur fichier>
    }
    finally {
     <fermeture du fichier>
    }
    .... suite



    Fonctionnement dans le cas où rien n'a lieu

    Si aucun incident ne se produit durant l'exécution du bloc < action sur fichier> :


    l'exécution se poursuit à l'intérieur du bloc finally par l'action <fermeture du fichier> et la suite du code.


     
    Fonctionnement si quelque chose se produit

    Si un incident se produit durant l'exécution du bloc < action sur fichier> et qu'une exception est lancée:


    malgré tout l'exécution se poursuivra à l'intérieur du bloc finally par l'action <fermeture du fichier>, puis arrêtera l'exécution du code dans le bloc.


    La syntaxe Java autorise l'écriture d'une clause finally associée à plusieurs clauses catch :

    try {
     <code à protéger>
    }
    catch (exception1 e ) {
      <traitement de l'exception1>}
    catch (exception2 e ) {
      <traitement de l'exception2>}
    ...
    finally {
      <action toujours effectuée>
    }

    Remarque :
    Si le code du bloc à protéger dans try...finally contient une instruction de rupture de séquence comme break, return ou continue, le code de la clause finally{...} est malgré tout exécuté avant la  rupture de séquence.


    Nous avons vu au chapitre 1.2 § 5.2 sur la boucle while, la boucle  for et l'instruction continue que  l'équivalence suivante entre un for et un while  valide dans le cas général, était mise en défaut si le corps d'instruction contenait un continue (instruction forçant l'arrêt d'un tours de boucle et relançant l'itération suivante) :


    Equivalence incorrecte si Instr contient un continue :
    for (Expr1 ; Expr2 ; Expr3 ) Instr  Expr1
    while ( Expr2
    { Instr ;
       Expr3
    }

    Equivalence correcte même si Instr contient un continue :
    for (Expr1 ; Expr2 ; Expr3 ) Instr  Expr1
    while ( Expr2
    {
      try {
        Instr ;
      } finally {    Expr3
        }
    }



    2. Un exemple complet de  traitement

      2.1 Enoncé

      Nous nous proposons de mettre en oeuvre les concepts précédents sur un exemple simulant un traitement de fichier. l'application est composée d'un bloc principal <programme> qui appelle une suite de blocs imbriqués. Les trois blocs du dernier niveau les plus internes <OuvrirFichier>,<LireElement> et <FermerFichier> peuvent lancer chacun une exception selon le schéma ci-après :

      <programme>
      ...<ActionsSurFichier>

      ........<AfficheFichier>

      ............<ChercheElement>
      ................<OuvrirFichier> --> exception
      ................<LireElement>   --> exception
      ................<FermerFichier> --> exception

    2.2 La démarche

    Les éventuelles exceptions lancées par les
    blocs <OuvrirFichier>,<LireElement> et <FermerFichier> doivent pouvoir se propager aux blocs de niveaux englobant afin d'être interceptable  à n'importe quel niveau.
     



    Les classes d'exception
    On propose de créer une classe générale d'exception EFichierError héritant de la classe des Exception de java, puis 3 classes d'exception héritant de cette classe EFichierError


    Modèle java pour ces 4 classes d'exception (à compléter) :

    class EFichierError extends Exception{
         public int typeErr; // stockage du numéro d'erreur pour utilisation ultérieure
          .........     }
       
    class EOuvertureError extends EFichierError{
           .........   }

    class ELectureError extends EFichierError{
           .........   }
       
    class EFermetureError extends EFichierError{
           .........    }

    Les blocs lançant éventuellement une exception
    Chaque bloc le plus interne peut lancer (lever) une exception de classe différente et la propage au niveau supérieur :



    Modèle java pour les appels de bloc et la propagation (à compléter) :

    void ChercheElement( ) throws .....{
             OuvrirFichier( );
             LireElement( );
             FermerFichier( );
          }
    void OuvrirFichier( ) throws .....{
             System.out.println("  >> Action ouverture...");
             GenererIncident(1);
             System.out.println("  >> Fin ouverture.");
          }
    void LireElement( ) throws .....{
             System.out.println("  >> Action lecture...");
             GenererIncident(2);
             System.out.println("  >> Fin lecture.");         
          }
    void FermerFichier( ) throws .....{
             System.out.println("  >> Action fermeture...");
             GenererIncident(3);
             System.out.println("  >> Fin fermeture.");         
          }     
    void GenererIncident(int TypeIncident) throws .....{
            selon la valeur de l'entier TypeIncident
            engendre aléatoirement ou non une exception de l'un des 3 types :
            EOuvertureError, ELectureError ou EFermetureError

            ......
          }

    Les blocs interceptant les exceptions

    Nous proposons par exemple d'intercepter les exceptions dans les deux blocs  <
    ActionsSurFichier> et <AfficheFichier> :

     



    Le bloc <AfficherFichier>

    Ce bloc interceptera une exception de type EFichierError, puis la redéclenchera après traitement :



    Modèle java pour ce bloc (à compléter) :

    void AfficherFichier( ) throws .....{
             try {
                ChercheElement( );
             }
             catch ( EFichierError E ){
                   .............
             }
    }

    Le bloc <ActionsSurFichier>

    Ce bloc interceptera une exception de l'un des trois types EOuvertureError,  ELectureError ou EFermetureError  :



    Modèle java pour ce bloc (à compléter) :

     void ActionsSurFichier( ) throws ......{
             System.out.println("Debut du travail sur le fichier.");
             try{
                System.out.println(".........");
                AfficherFichier( );
             }
             catch .......
             System.out.println("Fin du travail sur le fichier.");
          }

    Modèle java de la méthode main appelant le bloc <ActionsSurFichier> (à compléter) :

     public static void main(String[] arg){
             ..... Obj .......
             try {
                Obj.ActionsSurFichier( );
                }
             catch ......
          }


    2.3 Un programme Java : une solution

    Modèle java pour tous les blocs :





    Le programme Java

    Un code source java complet :

        class EFichierError extends Exception{
          public int typeErr;
           public EFichierError( String s, int x){
             super(s);
             typeErr = x;
          }
       }
       
        class EOuvertureError extends EFichierError{
           public EOuvertureError( int x){
             super("Impossible d'ouvrir le fichier !",x);
          }
       }

        class ELectureError extends EFichierError{
           public ELectureError( int x){
             super("Impossible de lire le fichier !",x);
          }
       }
       
        class EFermetureError extends EFichierError{
           public EFermetureError( int x){
             super("Impossible de fermer le fichier !",x);
          }
       }
      
        class PrTransmettre{
           void TraitementGen( String s){
             System.out.println("traitement general de l''erreur: "+s);
          }
           void TraitementSpecif(String s){
             System.out.println("traitement specifique de l''erreur: "+s);
          }
      
           void ChercheElement( ) throws EFichierError{
             OuvrirFichier();
             LireElement();
             FermerFichier();
          }
           void OuvrirFichier( ) throws  EFichierError{
             System.out.println("  >> Action ouverture...");
             GenererIncident (1);
             System.out.println("  >> Fin ouverture.");
          }
           void LireElement( ) throws  EFichierError{
             System.out.println("  >> Action lecture...");
             GenererIncident (2);
             System.out.println("  >> Fin lecture.");         
          }
           void FermerFichier( ) throws  EFichierError{
             System.out.println("  >> Action fermeture...");
             GenererIncident (3);
             System.out.println("  >> Fin fermeture.");         
          }     
           void GenererIncident ( int TypeIncident) throws EOuvertureError, ELectureError, EFermetureError {
    // engendre aléatoirement ou non une exception de l'un des 3 types
             int n;
             switch (TypeIncident){
                case 1: n = (int)(Math.random()*10)%4;
                   if (n==0) throw new EOuvertureError(TypeIncident);
                   break;
                case 2: n=(int)(Math.random()*10)%3;
                   if (n==0) throw new ELectureError(TypeIncident);
                   break;
                case 3:n=(int)(Math.random()*10)%2;
                   if (n==0) throw new EFermetureError(TypeIncident);
             }
          }
           void ActionsSurFichier( ) throws EFichierError{
             System.out.println("Debut du travail sur le fichier.");
             try{
                System.out.println(".........");
                AfficherFichier();
             }
                 catch(EOuvertureError E){
                   TraitementSpecif(E.getMessage( ));
                } 
                 catch(ELectureError E){
                   TraitementSpecif(E.getMessage( ));
                }
                 catch(EFermetureError E){
                   TraitementSpecif(E.getMessage( ));
                }
             System.out.println("Fin du travail sur le fichier.");
          }
           void AfficherFichier( ) throws EFichierError {
             try {
                ChercheElement();
             }
                 catch(EFichierError E){
                   TraitementGen(E.getMessage());
                   throw E;
                }
          }
           public static void main(String[] arg){
             PrTransmettre Obj = new PrTransmettre();
              try {
                  Obj.ActionsSurFichier();
                }
                catch (EFichierError E){
                 System.out.println("Autre type d'Erreur générale Fichier !");
                }
          }
       }
       
    Les exécutions du programme

    Exécution dans le cas où aucune exception n'est lancée :
    Debut du travail sur le fichier.
    .........
      >> Action ouverture...
      >> Fin ouverture.
      >> Action lecture...
      >> Fin lecture.
      >> Action fermeture...
      >> Fin fermeture.
    Fin du travail sur le fichier.

    Exécution dans le cas où une exception  ELectureError est lancée :
    Debut du travail sur le fichier.
    .........
      >> Action ouverture...
      >> Fin ouverture.
      >> Action lecture...
    traitement general de l''erreur: Impossible de lire le fichier !
    traitement specifique de l''erreur: Impossible de lire le fichier !
    Fin du travail sur le fichier.

    Exécution dans le cas où une exception  EOuvertureError est lancée :
    Debut du travail sur le fichier.
    .........
      >> Action ouverture...
    traitement general de l''erreur: Impossible d'ouvrir le fichier !
    traitement specifique de l''erreur: Impossible d'ouvrir le fichier !
    Fin du travail sur le fichier.

    Exécution dans le cas où une exception  EFermetureError est lancée :
    Debut du travail sur le fichier.
    .........
      >> Action ouverture...
      >> Fin ouverture.
      >> Action lecture...
      >> Fin lecture.
      >> Action fermeture...
    traitement general de l''erreur: Impossible de fermer le fichier !
    traitement specifique de l''erreur: Impossible de fermer le fichier !
    Fin du travail sur le fichier.