JAVEXCEP                                                            (c)~/2A env.  -  Création:  2002.11.01

Chap.  7 – Compl.                                                  Dernière MAJ:  01.12.2009 / H. Nguyen-Phu

 

 

           INTRODUCTION A LA GESTION  D'EXCEPTIONS

 

Plan:

------

     Introduction

 

     Les instructions de gestion des exceptions

         Interception d'exceptions par 'try' ... 'catch' ... 'finally'

         Le mot clé 'throws' pour relayer une exception

         Le mot clé 'throw'  pour lever une exception

 

     Classification des exceptions

         Exemple d'erreur dérivant de la classe 'java.lang.Error'

         Exemple d'exceptions dérivant de la classe 'RuntimeException' 

         Un exemple d'exception contrôlée: 'FileNotFoundException'

 

     Enrichissement de la hiérarchie des classes d'exceptions

 

     Annexe: Une arborescence partielle de la classe ' Throwable '

 

------------------------------------------------------------------------------

1. INTRODUCTION

***************

   Sous Java, on distingue deux sortes d'erreurs: les exceptions et les erreurs (cf. la classe 'java.lang.Error' et ses dérivées). Ces  dernières entraînent, en général, l'arrêt brutal de l'exécution d'un  programme. Quant aux erreurs "légères" ou exceptions, on peut considérer que ce sont des incidents qui peuvent interrompre son déroulement "normal".

 

   Au sein des exceptions, il existe en fait aussi deux types d'exceptions:

(i)  les exceptions "non contrôlées" qui dérivent toutes de la classe 'java.lang.RuntimeException' peuvent être seulement "interceptées"  ou relayées aux méthodes appelantes via la clause 'throws';

(ii) les autres exceptions, dites les exceptions "contrôlées" qui peuvent  être interceptées et aussi "traitées" via les clauses 'try' ... 'catch' ... 'finally' ou "levées (ou lancées)"  par un  développeur d'application via la clause 'throw'.

 

Par définition, une exception contrôlée est une exception qui DOIT être capturée ('catch') ou levée ('throw') par le programme applicatif.  Par la suite, on ne considère que les exceptions contrôlées de Java.

 

Une version simplifiée de la hiérarchie des classes d'erreurs  ('java.lang.Error') et d'exceptions ('java.lang.Exception')

qui dérivent toutes les deux de la classe 'Throwable' est donnée par:

 

Object

------

  |

  | java.lang.Throwable

   --------------------

      |

      | java.lang.Error

      | ---------------

      | java.lang.Exception

       --------------------

             |

             | java.lang.RuntimeException

             | --------------------------

             | "lesExceptionsContrôlées" (Ex. java.io.IOException ...)

               -------------------------

 

2. Les instructions de gestion des exceptions

*********************************************

2.1 Interception d'exceptions par 'ESSAI' ... 'CAPTURE' ... 'ENFIN'

===================================================================

    La syntaxe de cette instruction composée permet la gestion directe

d'une ou de plusieurs levées d'exceptions selon le schéma ci-dessous:

 

 try  {   // <=>  ESSAI  en L.A.O.

   // bloc d'instructions susceptible de générer des exceptions ...

 }

 catch (uneClasseDException  uneException_1)  {  // <=> CAPTURE( ... )

   // bloc correspondant à un type d'erreur No 1 ...

 }

 [ ... ]

 catch (uneAutreClasseDException  uneException_k)  {

   // bloc correspondant à un type d'erreur No k ...

 }

 finally  {   // <=>  ENFIN

  // séquence d'instructions à exécuter dans tous les cas ...

 }

 

 

Le fichier 'div_zero.java' donne justement un exemple d'emploi de ces

mot-clés:

 

 import java.io.*;

 class div_zero  {

   public static PrintWriter  jerr = new PrintWriter(System.err, true);

   public static void main(String[] arg)  {

     int zero = 0;

     jerr.println("Emploi de 'try...catch...finally' (c)~/2A env. \n");

     try  {

          zero = 2002/zero;

     }

     catch (ArithmeticException ae)  {

      jerr.println("Une exception arithmétique (non contrôlée)  est levée !");

      jerr.println("Message   : " + ae.getMessage());

      jerr.println("venant de  : " + ae.getClass());

      jerr.println("\nPile d'exécution : ");       ae.printStackTrace();

     }

     finally  {

      jerr.println("Démo. de 'try...catch...finally' terminée !");

     }

     es.attente(); // pause pour lire ...

   }

 }

 

EXERCICE 1: Re-prendre la résolution de l'équation du premier degré en traitant l'exception 'NumberFormatException' lors de la saisie des données réelles doubles par des caractères.

 

Solution typique (cf. fichier 'eq1_err.java')

----------------

 import java.io.*;

 class eq1_err  {

  // A fourth  Java version to compute 'ax+b = 0'

  static PrintWriter   jout = new PrintWriter(System.out, true);

  static PrintWriter   jerr = new PrintWriter(System.err, true);

  static BufferedReader jin = new BufferedReader(new InputStreamReader(System.in));

 

  public static void main(String [] args)  {

    double a, b;

    jout.print("eq1_err: Solving 'ax+b=0' with NumberFormatException -(c) ~/2A \n");

    try   {

      if (args.length == 0)  {

        jout.println("Under normal use, type:   java  eq1_err   value_a  value_b !");

        jout.println("Enter  a  and  b ('aaa'  or  'bbb' to throw an exception)  :");

        a = Double.parseDouble(jin.readLine());

        b = Double.parseDouble(jin.readLine());

      }

      else   {

        a = Double.parseDouble(args[0]);

        b = Double.parseDouble(args[1]);

      }

        jout.println("Solution x = "+ (-b)/a +'\n');

    }

    catch (IOException ioe)  {

        jerr.println("IOException warning : " + ioe.getMessage() +" !");

        jerr.println("Stack : "); ioe.printStackTrace();

    }

    catch (NumberFormatException nfe)  {

        jerr.println("NumberFormatException warning : " + nfe.getMessage() +" !");

        jerr.println("Stack : "); nfe.printStackTrace();

        jerr.println("\nBy default, a = 0.0  and b = -1.0 !");

        a = 0d;   b= -1d;

        jerr.println("By default x = "+ (-b)/a +'\n');

    }

    finally  {

        jout.println("Done !");

    }

    es.attente();

  }

 }

 

// Autre exemple d’emploi : cf. facrec.java

 

 

2.2 Le mot clé 'throws' pour relayer une exception

====================================================

    Pour qu'une méthode puisse déléguer le traitement d'une exception,  à un SSP appelant, elle doit le signaler par une clause 'throws' dans son en-tête de la manière suivante:

 

 [modificateurDeVisibilité] [TypeC] uneMéthode([listeDAguments])

    throws   uneClasseDException, uneAutreClasseDException ...

 {

    // bloc d'instructions correspondant ...

 }

 

Ainsi, on peut "externaliser" le traitement d'exception et le confier à un gestionnaire d'exception dédié se trouvant dans le(s) sous-programme(s) appelant(s) tant que la pile d'exécution n'est pas vide.  On parle alors de remontée (ou propagation) d'exception de la méthode appelée vers la méthode appelante. Si aucune méthode de la pile ne capture l'exception,  celle-ci provoque la fin de l'exécution et la génération d'un message  d'erreur.

 

 

2.3 Le mot clé 'throw'  pour lever une exception

    =============================================

    Pour provoquer une exception, on emploie la clause 'throw' (sans 's' ici) de la façon suivante:

 

  throw  new  uneClasseDException(uneListeDArguments);

 

Exemple d'emploi de 'throw' cf. FactRexI.java

---------------------------

 

 

3. Classification des exceptions

********************************

   On a distingué, lors de l'introduction, les deux types d'exceptions:

- les exceptions non contrôlées dérivant de 'RuntimeException' ou 'Error';

- les autres exceptions, dites exceptions contrôlées.

 

3.1 Exemple d'erreur dérivant de la classe 'java.lang.Error'

------------------------------------------------------------

    Ce sont des exceptions  très sérieuses liées à la machine virtuelle,  pour lesquelles il est souvent impossible de trouver une solution. C'est la raison pour laquelle elles ne sont ni ne doivent pas être interceptées par la clause 'catch' en général, sauf si on souhaite évaluer la taille mémoire allouée pour la pile d’exécution appelée aussi « pile système ». A titre d'exemple, on réalise une boucle "infinie" d'appels récursifs i.e. sans condition d'arrêt (c'est ce qu'il faut TOUJOURS adjoindre en pratique):

 

 class rec_infi  {// Récursivité infinie pour faire déborder la pile

// static int compt = 0;  

   static void methodeRecursive()  {

//      compt++;

//      System.out.println("Appel No "+compt);

        methodeRecursive();

   }

   public static void main(String[] a)  {

        rec_infi.methodeRecursive();

   }

 }

 

A l'exécution de la machine virtuelle, on rencontre l'erreur suivante:

 

  Exception in thread "main" java.lang.StackOverflowError

       at rec_infi.methodeRecursive(rec_infi.java : 6)

       at rec_infi.methodeRecursive(rec_infi.java : 6)

       ...

 

EXERCICE 2: Comment déterminer le nombre d'appels récursifs qui provoque le débordement de la pile ainsi que la taille en Ko de taille de pile système (A terminer en TPP) ?

 

 

 

3.2 Exemple d'exceptions dérivant de la classe 'RuntimeException'

-----------------------------------------------------------------

    L'exemple ci-dessous illustre trois exceptions fréquemment  rencontrées. L'emploi de 'try ... catch' n'est pas nécessaire ici, sauf pour pouvoir enchaîner toutes les erreurs au sein d'une seule et même trace d'exécution.

 

 class run_err { // Quelques exceptions non controlees du Runtime

    public static void main(String args[])   {

       System.out.println("run_err: Verif. de qq exceptions du Runtime");

       System.out.println("-------  (c)~/2A env.  2002.11.04     21H50");

       // ArrayIndexOutOfBoundsException

       char [] tab = {'I','n','f','o','r','m','a','t','i','q','u','e'};

       try  {

         for (int i=0; i<= tab.length; i++)

           System.out.print(tab[i]+" ");

       }

       catch (ArrayIndexOutOfBoundsException excep)  {

           System.err.println("\nErreur : "+ excep);

       }

       // StringIndexOutOfBoundsException

       String palin = "Leon noeL";

       try  {

         System.out.println("\npalindrome : "+ palin.toString() );

         System.out.println(palin.charAt( palin.length() ));

       }

       catch (StringIndexOutOfBoundsException excep)  {

           System.err.println("Erreur : "+ excep);

       }

       // NullPointerException

       String txt = null;

       try  {

         System.out.println("txt : "+txt.toString());

       }

       catch (NullPointerException excep)  {

           System.err.println("Erreur : "+ excep);

       }

       es.attente(); // Pour stabiliser l'écran avant de quitter ...

    }

 }

 

3.3 Un exemple d'exception contrôlée: 'FileNotFoundException'

------------------------------------------------------------  

EXERCICE 3: Faire l'analyse du programme ci-dessous (cf. 'fnf_err.java') en notant les trois emplois de "throws IOException"

 

import java.io.*;

 class fnf_err {

   private BufferedReader jin = null;

   private Reader  data = null;

   private String fileName;

 

   fnf_err() throws IOException  {

     jin = new BufferedReader(new InputStreamReader(System.in));

   }

 

   public void setFileName() throws IOException  {

     if (jin != null)

        fileName = jin.readLine();

   }

 

   public static void main(String args[]) throws IOException  {

     PrintWriter jout = new PrintWriter(System.out, true);

     fnf_err unObjet = new fnf_err();

     jout.println("Verif de FileNotFoundException - D'apres Leduc & Leduc");

     jout.println("-----   p. 96 Ed. Technip 2001 (c)~/2A env. 2002.11.04");

 

     while (unObjet.data == null)  {

       jout.println("\nEntrer un nom de fichier : ");

       unObjet.setFileName();

       try  {

         unObjet.data = new FileReader(unObjet.fileName);

       }

       catch  (FileNotFoundException excep)  {

         System.out.println( excep.getMessage() );

       }

     }

     jout.println("OK, le fichier existe ! A bientot ...");

     es.attente();

   }

 }

 

Indication: L'exception de type 'IOException' peut être levée par le  constructeur sans argument 'fnf_err()' et par la méthode 'setFileName()'. Elle n'est pas interceptée immédiatement par ces méthodes mais transmise à l'appelant qui n'est autre que la méthode principale  'main'. Dans ce cas précis, le 'main' ne l'intercepte pas non plus et,  en cas d'incident, l'exception provoque alors une interruption de l'exécution.

 

4. Enrichissement de la hiérarchie des classes d'exceptions

***********************************************************

  Tout programmeur Java peut enrichir la hiérarchie des classes d'exception en créant sa propre classe qui est toujours une sous-classe de 'Exception' selon l'exemple ci-après (cf. 'lever_ex.java'). On peut hériter ainsi des  deux constructeurs et des méthodes:

 

 - fillInStackTrace(),

 - getLocalizedMessage(),

 - getMessage(),

 - printStackTrace()  et

 - toString()

 

de la classe 'Throwable'. Une fois la nouvelle classe d'exception implémentée, on l'instancie (via 'new') et on peut la lancer (via la clause 'throw') comme dans le corps de la "methode_aa()" ci-dessous:

 

 class maGestionDException  extends  Exception  {

    maGestionDException()  {  // constructeur par défaut ...

    }

    maGestionDException(String unMessage)  {

        super(unMessage);

    }

 }

 class lever_ex  {

   lever_ex()  throws  maGestionDException  {

       methode_a();

   }

   void methode_a()  throws  maGestionDException  {

       methode_aa();

   }

   void methode_aa()  throws  maGestionDException  {

       throw new maGestionDException("Voici le descriptif : ...");

   }

   public static void main (String [] arg)   {

       try  {

           new leve_ex();

       }

       catch (maGestionDException  excep_interceptee)  {

              excep_interceptee.printStackTrace();

       }

    }

 }

 

Autre exemple:  cf. FactRexI.java

 

5. Annexe: Une arborescence partielle de la classe 'Throwable'

**************************************************************

   Une version plus détaillée de la hiérarchie des classes d'erreurs et d'exceptions est donnée par (cf. la documentation de l'API pour le reste des sous-classes):

 

Object

------

  |

  | java.lang.Throwable

      |

      | java.lang.Error

      |      |

      |      | java.awt.AWTError

      |      |

      |      | java.lang.LinkageError    

      |      |     | java.lang.ClassCirculariryError

      |      |     | java.lang.ClassFormatError

      |      |     | java.lang.ExceptionInInitializerError

      |      |     | java.lang.IncompatibleClassChangeError

      |      |     |     | java.lang.AbstractMethodError

      |      |     |     | java.lang.IllegalAccessError

      |      |     |     | // tentative d'accès est interdite

      |      |     |     | java.lang.InstantiationError

      |      |     |     | java.lang.NoSuchFieldError

      |      |     |     | java.lang.NoSuchMethodError

      |      |     |     | //la méthode référencée n'est pas accessible - Ex. : main

      |      |     |

      |      |     | java.lang.NoClassDefFoundError

      |      |     | java.lang.UnsatisfiedLinkError

      |      |     | java.lang.VerifyError

      |      |       

      |      | java.lang.ThreadDead

      |      |

      |      | java.lang.VirtualMachineError

      |      |     | java.lang.InternalError

      |      |     | java.lang.OutOfMemoryError

      |      |     | // allocation impossible par manque de mémoire

      |      |     | java.lang.StackOverflowError

      |      |     | // il y a débordement de la pile système

      |      |     | java.lang.UnknownError

      |

      | java.lang.Exception

             |

             | java.awt.AWTException

             | java.swing.text.BadLocationException

             | java.lang.ClassNotFoundException

             | java.lang.CloneNotSupportedException

             | java.util.zip.DataFormatException

             | java.security.GeneralSecurityException

             | java.lang.IllegalAccessException         

             | java.lang.InstantiationException

             | java.lang.InterruptedException 

             |

             | java.io.IOException

             |     | javax.swing.text.ChangedCharSetException

             |     | java.io.CharConversionException

             |     | java.io.EOFException

             |     | java.io.FileNotFoundException // fic. manquant

             |     | java.io.InterruptedIOException

             |     | java.net.MalformedURLException

             |     | java.io.ObjectStreamException

             |     | java.net.ProtocolException

             |     | java.rmi.RemoteException

             |     | java.net.SocketException

             |     | java.io.SyncFailedException

             |     | java.net.UnknownHostException

             |     | java.net.UnknownServiceException

             |     | java.io.UnsupportedEncodingException

             |     | java.io.UTFDataFormatException

             |     | java.util.zip.ZipException

             |

             | javax.naming.NamingException

             | java.lang.NoSuchFieldException

             | java.lang.NoSuchMethodException

             | java.rmi.NotBoundException

             | java.text.ParseException

             | java.awt.print.PrinterException

             | java.beans.PropertyVetoException

             |

             | java.lang.RuntimeException

             |     | java.lang.ArithmeticException

             |     | // erreur arithmétique est survenue (Ex.: DIV par 0)

             |     | java.lang.ArrayStoreException

             |     | java.lang.ClassCastException

             |     | java.lang.IllegalArgumentException

             |     |     | java.lang.IllegalThreadStateException

             |     |     | java.lang.NumberFormatException

             |     |     | // Ex.: une chaîne de car. est incorrecte pour

             |     |     | //      représenter un nombre

             |     |

             |     | java.lang.IllegalMonitorStateException

             |     | java.lang.IllegalStateException

             |     | java.lang.IndexOutOfBoundsException

             |     |     | java.lang.ArrayIndexOutOfBoundsException

             |     |     | // indice d'un tableau est en dehors des bornes

             |     |     | java.lang.StringIndexOutOfBoundsException

             |     |       // indice de chaîne est en dehors des bornes

             |     | java.lang.NegativeArraySizeException

             |     | java.lang.NoSuchElementException

             |     |

             |     | java.lang.NullPointerException

             |     | // tentative d'accès à un objet avec référence 'null'

             |     |

             |     | java.lang.SecurityException

             |     | java.lang.UnsupportedOperationException

             |     | java.util.EmptyStackException

             |     | java.util.NoSuchElementException

             |     |     | java.util.InputMismatchException  // cf. Java 5

             |

             |

             | java.sql.SQLException

 

 

 

Les traits continus correspondent au mot clé 'extends' pour signifier qu'une sous-classe "dérive de" sa super-classe en mode d'héritage simple.  Toutes les sous-classes ci-dessus implémentent aussi la classe  'Serializable'. Les exceptions en caractère gras sont les plus souvent commises en 2A.