Gestion des exceptions Même lorsqu’un programme est terminé, certaines circonstances exceptionnelles peuvent compromettre l’exécution de celui-ci. Données incorrectes, fin de fichier prématuré, etc.. Plusieurs langages disposent d’un mécanisme très souple nommé gestion des exceptions et qui permet de dissocier la détection d’une anomalie de son traitement, ce qui permet d’avoir une plus grande lisibilité des programmes. Gestion des erreurs avec des conditions Avec le gestionnaire d’exception errorCodeType readFile { readFile { try { open the file; determine its size; allocate that much memory; read the file into memory; close the file; initialize errorCode = 0; open the file; if (theFileIsOpen) { determine the length of the file; if (gotTheFileLength) { allocate that much memory; if (gotEnoughMemory) { read the file into memory; if (readFailed) { errorCode = -1; } } else { errorCode = -2; } } else { errorCode = -3; } close the file; if (theFileDidntClose && errorCode==0){ errorCode = -4; } else { errorCode = errorCode and -4; } } else { errorCode = -5; } return errorCode; } catch (fileOpenFailed) { doSomething; } catch (sizeDeterminationFailed) { doSomething; } catch (memoryAllocationFailed) { doSomething; } catch (readFailed) { doSomething; } catch (fileCloseFailed) { doSomething; } } } Une exception est une rupture de séquence déclenchée par une instruction throw comportant une expression de type classe. Il y a alors un branchement à un ensemble d’instructions try catch et le choix du bon gestionnaire d’exception est fait en fonction du type de l’objet mentionné par le throw. On trouve 2 types d’exception : celle qui peuvent être évitées en améliorant le code et celles qui ne le peuvent pas. Les premières sont nommées exceptions non vérifiées, par exemple, lorsqu’on veut diviser par 0, le système génère une ArithmeticException. Dans ce cas-ci, nous éviterons de diviser par 0 en améliorant le code. Les secondes sont nommées exceptions vérifiées et elles sont vérifiées par le compilateur avant l’exécution du programme. Les instructions qui les lancent doivent être insérées dans un bloc try. . Page 1 Exceptions non vérifiées (unchecked) Les erreurs provoquées par une exception non vérifiée peuvent être évitées, car elles sont souvent provoquées par le programme. Exemple class TestException{ public static double sqrt(double x){ if(x < 0) throw new IllegalArgumentException(); return Math.sqrt(x); } public static void main(String[] args){ try{ System.out.println(sqrt(-25)); } catch(Exception e){ System.out.println("exception : " + exception); } System.out.println("Fin du test"); } } Résultat en console Exception : java.lang.IllegalArgumentException Fin du test Par contre, ce genre de programme n’est pas réaliste, car ces erreurs peuvent être anticipées et donc gérées par un code standard. Ex : ne pas faire sqrt si x = 0; Exceptions vérifiées (checked) Les erreurs provoquées par une exception vérifiée ne peuvent généralement être évitées, car elles sont souvent provoquées par un accès externe. Un try catch doit être ajouté. Exemple public static void main(String[] args){ int n = 0; try{ String input = args[0]; System.out.println("input = " + input); n = Integer.parseInt(input); } catch(ArrayIndexOutOfBoundsException e){ System.out.println("Pas d’entree : " + e); } catch(NumberFormatException e){ System.out.println("Conversion impossible : " + e); } finally{ System.out.println("n = " + n); } } Résultat en console si on ne passe aucun paramètre en ligne de commande Pas d’entree : java.lang.ArrayIndexOutOfBoundsException n = 0 Résultat en console si on passe #123 en paramètre Input = #123 Conversion impossible : java.lang.NumberFormatException n = 0 Résultat en console si on n’entre 123 en paramètre Input = 123 n = 123 Page 2 Il peut y avoir plusieurs catch, mais 1 seul sera exécuté selon l’exception lancé par le throw. Il peut y avoir qu’un seul finally mais il est optionnel. S’il est présent, il est toujours exécuté. Pour chaque try, il doit avoir au moins 1 catch ou un finally. Exemple avec les tableaux public static void main(String[] args){ Scanner sc = new Scanner(System.in); System.out.print("Entrez la taille du tableau :"); int indice = sc.nextInt(); try{ int tab[] = new int[indice]; //1ère exception for(int i=0; i <= indice; i++){ //2e exception tab[i] = i + 1; System.out.println("tab[i] = " + tab[i]); } } catch(NegativeArraySizeException e){ System.out.println("Taille du tableau négative"); } catch(ArrayIndexOutOfBoundsException e){ System.out.println("Indice en dehors du tableau"); } } Exemple avec les fichiers try{ reader = new FileReader(filename); Scanner in = new Scanner(reader); readData(in); reader.close();//May never get here }catch(Exception exception){ exception.printStackTrace(); } try{ reader = new FileReader(filename); Scanner in = new Scanner(reader); readData(in); }catch(Exception exception){ exception.printStackTrace(); }finally{ reader.close();//toujours exécutée! } Autre exemple avec les fichiers public static void main() { BufferedReader entree; String line, fichier = JOptionPane.showInputDialog("Fichier :"); try{ entree = new BufferedReader(new FileReader(fichier)); while((line = entree.readLine()) != null){ System.out.println(line); } }catch(FileNotFoundException fnf){ System.err.format("File: %s not found%n", fichier); }catch(IOException e){ System.err.println(e.toString()); }finally{ if(entree != null){ try{ entree.close(); }catch(IOException io){} } } } Page 3 Java fournit de nombreuses classes prédéfinies dérivées de la classe Exception que voici : Exemple des principales exceptions Survient quand une opération arithmétique n’a pas pu être exécutée. try { ArithmeticException int c = 99 / 0; } catch (ArithmeticException e) { System.out.println("div by 0: " + e); } Survient quand un index invalide est utilise sur un tableau, un ArrayIndexOutOfBoundsException Array ou une String try { ou int c[] = {1,2,3}; IndexOutOfBoundsException c[42] = 99; ou } catch (IndexOutOfBoundsException e) { StringIndexOutOfBoundsException System.out.println("index invalid : " + e); } ArrayStoreException Survient lorsqu’un mauvais objet tente d’être stocké dans un Array. NegativeArraySizeException Survient si un array est créé avec une taille négative. Survient lorsqu’une application tente de loader une classe qui n’est pas trouvée. try{ ClassNotFoundException Class.forName("com.mysql.jdbc.Driver"); }catch(ClassNotFoundException e){ System.out.println("Driver not found"); } Survient lorsqu’on tente de convertir une string dans un format numérique mais que la string initiale n’est pas un nombre. try{ NumberFormatException int i = Integer.parseInt("HELLO"); }catch(NumberFormatException e){ e.printStackTrace(); } Survient lorsqu’on ouvre un fichier qui n’existe pas. try{ FileNotFoundException file = new FileInputStream("abc.txt"); }catch(FileNotFoundException e){ System.out.println("File does not exist."+e); } Survient lorsque la lecture ou l’écriture ne fonctionne try{ IOException while((k = file.read() ) != -1){ System.out.print((char)k); } file.close(); }catch(IOException e){ System.out.println("IO problem. " + e); } Page 4 La hiérarchie des exceptions Toutes les exceptions dérivent de la classe Throwable. La classe Error représente une erreur grave intervenue dans la machine virtuelle Java ou dans un sous-système Java. L'application Java s'arrête instantanément dès l'apparition d'une exception de la classe Error. La classe Exception représente des erreurs moins graves. Les exceptions héritant de la classe RuntimeException n'ont pas besoin d'être détectées impérativement par des blocs try/catch. Les exceptions de type Error et RuntimeException sont dites unchecked exceptions car les méthodes n'ont pas d'obligation à les traiter ou à déclarer leur propagation explicitement. Il n'est pas recommandé de créer ses propres exceptions en dérivant d'une exception de type RuntimeException. Même si cela peut sembler plus facile puisqu'il n'est pas obligatoire de déclarer leur propagation, cela peut engendrer certaines difficultés comme: oublier de traiter cette exception ne pas savoir que cette exception peut être levée par une méthode. Cependant, l'utilisation d'exceptions de type unchecked se répend de plus en plus depuis la diffusion de la plate-forme .Net qui ne propose que ce type d'exceptions. Créer vos propres exceptions Il est préférable d'utiliser les exceptions fournies par Java lorsqu'une de ces exceptions répond au besoin plutôt que de définir sa propre exception. Vous pouvez créer une classe qui hérite de la classe exception qui sera créé et lancer par vos objets pour être attrapée et plus précise pour vos propres exceptions. Pour créer la nouvelle exception il faut : Fournir deux constructeurs Un constructeur qui accepte une String pour décrire la raison de l’exception Exemple public class InsufficientFundsException extends Exception { public InsufficientFundsException() {} public InsufficientFundsException(String message) super(message); { } } Et elle sera lancée par un objet qui gère les comptes en banque Exemple if(amount > balance){ throw new InsufficientFundsException( "withdrawal of " + amount + " exceeds balance of " + balance); } Page 5