TSIG2 DA DAIGL S36 Cours n°1 3ème partie 5. Le traitement des erreurs (exceptions) Java prévoit un mécanisme pour le traitement des anomalies qui peuvent survenir lors de l'exécution du programme. C'est ce qu'on appelle le traitement des exceptions. Les exceptions peuvent être des erreurs graves qui devront généralement conduire à l'arrêt du programme ou des événements qui seront traités de sorte qu'ils ne provoquent pas l'arrêt du programme, par exemple : erreur de format de donnée, division par zéro, dépassement d’indice dans un tableau ou une chaîne … 5.1. Le renvoi des exceptions Si une exception n'est pas traitée dans la méthode dans laquelle elle survient, il faut qu’elle soit renvoyée à la méthode du niveau immédiatement supérieur et ainsi de suite jusqu’à ce qu’une méthode la traite. Enfin si l'exception sort de la méthode main non encore traitée, elle est alors traitée par la machine virtuelle Java. Le renvoi (ou l’outrepassement) des exceptions se fait explicitement en indiquant throws TypeException dans l’en-tête de la méthode (exemple : public void saisie throws IOException) ou bien implicitement pour les exceptions les plus courantes (ces dernières sont effectivement renvoyées à la méthode appelante si elles ne sont pas traitées dans la méthode qui les voit naître, sans que le programmeur n’ait à préciser quoi que ce soit). 5.2.La capture des exceptions Pour cela, Java introduit des mot-clés spéciaux que nous allons aborder ci-dessous. Le mot-clé try indique que le code qui suit est susceptible de générer une exception. L'interception de cette exception se fera par le mot-clé catch et l'action correspondante sera codée immédiatement après le mot-clé catch. La syntaxe est la suivante : try { // bloc de code pouvant générer l'exception } catch (TypeException e) { // traitement approprié } catch (TypeException e) { 1 TSIG2 DA DAIGL S36 Cours n°1 3ème partie ... } // Le bloc finally est facultatif. Il est exécuté dans tous les cas et en dernier. En fait, il sera // rarement présent, on le code par exemple pour la fermeture des fichiers. finally { ... } Chacune des instructions try, catch et finally doit être suivie d'un bloc { ... } même si ce bloc ne comporte qu'une seule instruction. Si une exception est générée dans les instructions du bloc try, elle est alors interceptée par le bloc catch correspondant (on sait qu'un catch est destiné à capturer un certain type d'exception d'après la classe de l'exception mentionnée après ce catch) s'il existe. Si on souhaite que catch capture n’importe quelle exception, on indique Exception comme type d’exception catch (Exception e) Exemple : calcul de la factorielle import java.io.*; class Factorielle { public static void main(String args[]) throws IOException { BufferedReader rep = new BufferedReader(new InputStreamReader(System.in)); int nombre, res = 1; System.out.println("Saisir le nombre dont il faut calculer la factorielle"); do { try { nombre = Integer.parseInt(rep.readLine()); } catch (NumberFormatException e) { nombre = -1; } } while (nombre < 0 || nombre > 10); for (int i=2; i<=nombre; i++) res *= i; System.out.println("Factorielle = " + res); } } 2 TSIG2 DA DAIGL S36 Cours n°1 3ème partie Exemple : calcul de la moyenne d’une liste d’entiers transmis en paramètres class Essaiexceptions { public static void main(String[] argv) { try { System.out. println("La moyenne est " + moyenne(argv)); } catch (ArithmeticException e) { System.out. println("Paramètres absents ou tous incorrects"); } } // Le throws ArithmeticException est facultatif car comme il s’agit d’une erreur // considérée comme courante, elle est renvoyée implicitement à la méthode appelante // si elle n’a pas été traitée dans la méthode qui l’a vue survenir. static int moyenne(String[] liste) throws ArithmeticException { int somme = 0, entier, nbNotes = 0; for (int i = 0; i < liste.length;i++) { try { entier = Integer.parseInt(liste[i]); somme += entier; nbNotes++; } catch (NumberFormatException e) { System.out.println("La note numero "+(i+1)+ " n'est pas entiere"); } } return somme/nbNotes; } } Déclaration d'exceptions Avant de voir comment déclarer une exception dans une méthode, il faut savoir comment lancer une exception. Prenons l'exemple suivant: 3 TSIG2 DA DAIGL S36 Cours n°1 3ème partie // DiviseParZero.java class DiviseParZero { public static void main (String args[]) { int zero=0; try { zero = 2002/zero; } catch (ArithmeticException e ) { System.out.println("Une exception arithmetique a ete lancee"); System.out.println("Message : " + e.getMessage()); System.out.println("Pile :"); e.printStackTrace(); } } } Java utilise pour lancer une exceptions le mot clé throw. Par exemple, si nous ajoutons dans notre programme la ligne suivante juste avant le calcul provoquant l'exception arithmétique (dans le bloc try): if (zero==0 ) throw new ArithmeticException ("Division par zero"); On obtient : Une exception arithmétique a été lancée Message : Division par zéro Pile : java.lang.ArithmeticException: Division par zéro at DiviseParZero.main(DiviseParZero.java:8) Cette fois-ci, nous avons nous même lancé cette exception, en indiquant un message précisant la nature exacte de l'erreur, redéfinissant l'exception par défaut. 5.3.Définir ses propres exceptions (compléments à revoir après cours sur l’héritage) Il est possible de définir sa propre exception. Pour ce faire, il suffit de définir sa classe exception, qui devra dériver de la classe Exception. Ensuite, nous allons créer une fonction qui va lever notre exception. Pour ce faire, il est obligatoire de préciser dans la signature de la fonction la liste des exceptions susceptibles d'être lancées. Voici le source de notre exemple : 4 TSIG2 DA DAIGL S36 Cours n°1 3ème partie // CreeException.java class NouvelleException extends Exception { NouvelleException () {} NouvelleException (String msg) { super(msg); } } public class CreeException { static void fnct () throws NouvelleException { // On lance l'exception throw new NouvelleException ("Exception nouvelle lancee"); } public static void main (String argv[]) { try { // On appelle la fonction qui lance l'exception fnct(); } catch (NouvelleException e) { // On attrape cette exception e.printStackTrace(); } } } Analysons cet exemple. Tout d'abord, nous déclarons notre nouvelle exception en créant une classe dédiée, héritée de la classe Exception. On crée deux constructeurs, l'un sans argument qui ne fait rien, l'autre prenant un message en argument. Ce message est transmis au constructeur de la classe parente, grâce au mot clé super() que nous avons vu plus haut. Ensuite, nous définissons une classe, dans laquelle on crée une méthode qui lance l'exception que nous venons de définir. Comme nous l'avons dit précédemment, on spécifie quelles exceptions peuvent être lancées dans la signature de la fonction, soit ici: static void fnct () throws NouvelleException { Cette fonction se contente de lever l'exception NouvelleException. Enfin, dans la méthode main(), nous faisons appel à la fonction précédente en installant un gestionnaire d'exceptions. Le bloc catch() attrape notre exception et affiche à l'écran le contenu de la pile d'appel des fonctions : NouvelleException: Exception nouvelle lancee at CreeException.fnct(CreeException.java:14) at CreeException.main(CreeException.java:20) 5 TSIG2 DA DAIGL S36 Cours n°1 3ème partie Remarquons que notre message spécifié lors de la lancée de l'exception a été reproduit. On note également que la pile d'appel contient deux fonctions, celle dans laquelle on lève l'exception, et celle appelant cette dernière, avec indication des numéros de lignes respectifs Annexe- Le tableau suivant propose quelques exemples d'exceptions que l’on peut traiter. SQLException erreurs SQL IOException erreurs d’entrées/sorties NullPointerException adresse nulle OutOfMemoryException adresse invalide ArithmeticException erreur sur opération ArrayIndexOutOfBoundsException Sortie du tableau ArrayStoreException Element incompatible negativeArraySizeException taille négative pour un tableau 6