Exceptions et assertions
Transcription
Exceptions et assertions
Exceptions, Assertions Programmation Orientée Objet Exceptions et assertions Frédéric Mallet (sur la base du cours de Richard Grin) http://deptinfo.unice.fr/~fmallet/ F. Mallet - POO 1 Exceptions, Assertions Objectifs Robustesse et Exceptions Error, RuntimeException, Exceptions (non) contrôlées Try-catch, finally, ressources Correction et Assertions Mot clé assert F. Mallet - POO 2 Exceptions, Assertions Fiabilité: robustesse vs. correction Correction Donne les résultats corrects lorsqu’il fonctionne en mode “normal” Les assertions permettent de vérifier qu’on est dans un mode “normal” Robustesse Fonctionne même lorsque l’environnement ne respecte pas le contrat (données erronées) Les exceptions permettent de détecter des évènements exceptionnels et maintenir le mode “normal” F. Mallet - POO 3 Exceptions, Assertions Représentent des évènements anormaux du système EXCEPTIONS F. Mallet - POO 4 Exceptions, Assertions Les exceptions sont des objets La classe Exception représente des évènements anormaux (qui ne doivent pas se produire). Il faut décrire le fonctionnement normal du programme • Bloc try Prévoir (éventuellement) des situations anormales • Bloc catch Réagir à ces situations anormales pour pallier aux erreurs éventuelles (normalement exceptionnelles/rares) de l’environnement On peut lever/lancer les exceptions (instances) throw e F. Mallet - POO 5 Exceptions, Assertions NumberFormatException: try/catch On peut attraper les exceptions pour détecter une erreur class Somme { static public void main(String[] args) { try { int somme = 0; for(String arg : args) somme += Integer.parseInt(arg); try: Fonctionnement normal System.out.println(somme); } catch (NumberFormatException nfe) { System.err.println(arg+" n’est pas un nombre!"); } } } Test : catch: Fonctionnement exceptionnel en cas d’erreur java Somme 12 pasUnNombre 14 pasUnNombre n’est pas un nombre! F. Mallet - POO 6 Exceptions, Assertions Synopsis: try-catch try { // fonctionnement normal … } catch (XXXException e) { // cas si XXXException se produit } catch (YYYException e) { // cas si YYYException se produit } Attention: On ne peut pas attraper une exception qui ne peut pas être levée ! (erreur de compilation) XXXException peut être une sous-classe de YYYException YYYException ne peut pas être une sous-classe de XXXException Les exceptions levées en dehors de try (avant, dans le catch, après) ne sont pas attrapées F. Mallet - POO 7 Exceptions, Assertions Traitement des exceptions Une méthode peut décider D’attraper les exceptions lancées et les traiter De propager les exceptions • Il faut le déclarer dans la signature de la méthode selon la nature • Ex: File (String pathname) throws NullPointerException D’attraper et les relancer ou d’en lancer d’autres (à déclarer) Propagation C’est la méthode appelante qui la récupère La méthode main peut décider aussi de propager F. Mallet - POO 8 Exceptions, Assertions Traitement des exceptions Dans un bloc try Le code entre l’instruction qui a levée l’exception et la fin du bloc try n’est pas exécuté Le premier bloc catch compatible est exécuté L’exécution se poursuit normalement après le dernier bloc catch ! F. Mallet - POO 9 Exceptions, Assertions Traitement des exceptions Pas d’exception levée : Si aucune exception n’est levée dans le bloc try Alors les blocs catch sont ignorés L’exécution se poursuit normalement après le dernier bloc catch • Continue/break/return F. Mallet - POO 10 Exceptions, Assertions Traitement des exceptions Exception pas attrapée Certaines exceptions ne sont pas obligatoirement attrapées (RuntimeException) Le message porté par l’exception est affiché Le Thread dans lequel l’exception a été lancée meurt • Les autres continuent leur exécution Dans la méthode main La méthode main peut aussi propager les exceptions ! F. Mallet - POO 11 Exceptions, Assertions java.lang.Throwable Toutes les exceptions sont de type Throwable Throwable Error Exception RuntimeException XXXException YYYException F. Mallet - POO 12 Exceptions, Assertions java.lang.Error Toutes les exceptions sont de type Throwable Throwable Erreurs graves JVM Error Exception RuntimeException XXXException YYYException F. Mallet - POO 13 Exceptions, Assertions Error Réservées aux erreurs de la JVM OutOfMemoryError NoSuchMethodError Recommandations Ne devraient pas arriver Très rare qu’on les attrape Ne devraient pas être lancées par les utilisateurs Pas de raison de faire des classes filles F. Mallet - POO 14 Exceptions, Assertions java.lang.RuntimeException Toutes les exceptions sont de type Throwable Throwable Error Exceptions noncontrôlées par le compilateur => Pas de try/catch Exception RuntimeException XXXException YYYException F. Mallet - POO 15 Exceptions, Assertions Exemples Usage A priori si l’on pense que le problème ne pourra pas être résolu (dans un catch) Pour ne pas forcer à alourdir le code Quelques RuntimeException NullPointerException ArrayIndexOutOfBoundsException NumberFormatException ClassCastException F. Mallet - POO 16 Exceptions, Assertions Exceptions contrôlées Toutes les exceptions sont de type Throwable Throwable Error Exception RuntimeException XXXException Exceptions contrôlées => try/catch obligatoire F. Mallet - POO 17 Exceptions, Assertions Exceptions contrôlées Définition Doivent être • Soit attrapées (try/catch) • Soit propagées (throws dans la signature) Contaminant vers la méthode appelante Attention Peut avoir un throws sans lancer l’exception Peut propager plusieurs exceptions dans un throws Ne peut pas avoir un catch si l’exception ne peut pas être lancée Quelques exceptions contrôlées ClassNotFoundException IOException (toutes celles de java.io) • EOFException • FileNotFoundException InterruptedException F. Mallet - POO 18 Exceptions, Assertions Lancer une exception L’utilisateur peut lancer des exceptions Soit en utilisant les classes prédéfinies Soit en définissant de nouvelles classes qui héritent de RuntimeException ou Exception Peut avoir à instancier un nouvel objet Exemple: class XXX implements Iterator<X> { @Override public void remove() { throw new UnsupportedOperationException("…"); } } F. Mallet - POO 19 Exceptions, Assertions Exemple: CompteBancaire Le crédit d’un montant négatif n’est pas prévu class CompteBancaire { int solde = 0; void créditer(int somme) { if (somme<0) throw new IllegalArgumentException("somme négative illégale!"); solde += somme; } } Note: IllegalArgumentException est une F. Mallet - POO RuntimeException 20 Exceptions, Assertions Définir ses propres exceptions Exception non-contrôlée class DécouvertException extends RuntimeException { CompteBancaire compte; DécouvertException(CompteBancaire c) { super("Découvert non autorisé sur "+c); this.compte = c; } } Usage class CompteBancaire { int solde = 0; void débiter(int somme) { if (somme>solde) throw new DécouvertException(this); solde -= somme; } } F. Mallet - POO 21 Exceptions, Assertions Exception et redéfinition Quand on redéfinit une méthode, on ne peut pas déclarer plus d’exceptions contrôlées que la méthode redéfinie Il faut renvoyer : les mêmes exceptions Ou des sous-classes de ces exceptions Ou moins d’exceptions (éventuellement aucune) F. Mallet - POO 22 Exceptions, Assertions Exceptions dans les constructeurs Si une exception est levée dans un constructeur Et qu’elle n’est pas attrapée Alors aucune instance n’est créée F. Mallet - POO 23 Exceptions, Assertions Définir ses propres exceptions Exception contrôlée class DécouvertException extends Exception { CompteBancaire compte; DécouvertException(CompteBancaire c) { super("Découvert non autorisé sur "+c); this.compte = c; } } Usage class CompteBancaire { int solde = 0; void débiter(int somme) throws DécouvertException { if (somme>solde) throw new DécouvertException(this); solde -= somme; } } F. Mallet - POO 24 Exceptions, Assertions Pré et Post-conditions LES ASSERTIONS F. Mallet - POO 25 Exceptions, Assertions Mot clé assert Depuis le JDK 1.4 assert(boolean_expression); assert(boolean_expression): "message d’erreur"; Permet de s’assurer qu’une propriété est vraie Si l’expression booléenne est vraie, rien ne se passe Sinon, une AssertionError est lancée Usage Pré-condition: au début d’une méthode Post-condition: à la fin d’une méthode Invariant: au milieu d’un code compliqué (boucle) F. Mallet - POO 26 Exceptions, Assertions Exemple: CompteBancaire Le crédit d’un montant négatif n’est pas prévu class CompteBancaire { int solde = 0; void créditer(int somme) { assert(somme>=0): "somme négative illégale :"+somme; solde += somme; } } Note: En général, ce sera garanti par l’interface graphique (pas encore disponible) F. Mallet - POO 27 Exceptions, Assertions Activation Activation Lors de la compilation (période de test) • Si on n’active pas, le code est ignoré (pas dans le bytecode) • Si on l’active, on peut choisir ou non de l’exécuter Lors de l’exécution • Si elles ont été activées à la compilation • Si on n’active pas, alors le code est ignoré • Si on active, cela peut produire des exceptions Test vs. Production Pendant les tests, les assertions sont activées En production, les assertions sont normalement désactivées F. Mallet - POO 28 Exceptions, Assertions Activation: compilation Pour activer les assertions javac –source 1.4 F. Mallet - POO 29 Exceptions, Assertions Activation: exécution Pour activer les assertions java –ea Pour activer pour un paquetage ou une classe java –ea<nom-paquetage>… java –ea<nom-classe> Pour désactiver pour un paquetage ou une classe java –ea -da<nom-paquetage>… java –ea -da<nom-classe> F. Mallet - POO 30 Exceptions, Assertions Activation: exécution Pour activer les assertions systèmes java –esa Pour désactiver les assertions systèmes java –dsa F. Mallet - POO 31 Exceptions, Assertions Pas une bonne idée pour un assert ! Exception contrôlée class DécouvertException extends Exception { CompteBancaire compte; DécouvertException(CompteBancaire c) { super("Découvert non autorisé sur "+c); this.compte = c; } } Usage class CompteBancaire { int solde = 0; void débiter(int somme) throws DécouvertException { if (somme>solde) throw new DécouvertException(this); solde -= somme; } } F. Mallet - POO 32 Exceptions, Assertions Compléments et nouveautés du JDK 7 EXCEPTIONS - COMPLÉMENTS F. Mallet - POO 33 Exceptions, Assertions Synopsis: multi-catch try { // fonctionnement normal … } catch (XXXException | YYYException e) { // cas si XXXException ou YYYException se produit } Attention: [Depuis JDK1.7] e est final XXXException : pas un sous-type de YYYException YYYException : pas un sous-type de XXXException Même comportement dans les deux cas ! Tentative pour éviter try { } catch(Exception e) F. Mallet - POO { } 34 Exceptions, Assertions Propager les exceptions Il ne faut pas attraper les exceptions trop vite Choisir le niveau le mieux adapté pour retrouver un état « normal » On attrape que si l’on est sûr de ne pas avoir un état dégradé après Au plus près de la cause de l’exception Les blocs try et catch sont des blocs d’instructions Les variables déclarées dans ces blocs meurent à la fin du bloc Try et catch sont deux blocs séparés ! • Le bloc catch ne connaît pas les variables déclarées dans le bloc try F. Mallet - POO 35 Exceptions, Assertions Throwable.printStackTrace() La méthode printStackTrace Affiche l’état de la pile au moment de l’exception C’est-à-dire la liste des méthodes qui ont conduit à l’exception (avec le numéro de ligne) Voir aussi Throwable.printStackTrace(PrintWriter) java.io.FileNotFoundException: ..\reference.txt.cs (Le fichier spécifié est introuvable) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(Unknown Source) at java.util.Scanner.<init>(Unknown Source) at fr.unice.cesar.BarChart.init(BarChart.java:36) at sun.applet.AppletPanel.run(Unknown Source) at java.lang.Thread.run(Unknown Source) F. Mallet - POO 36 Exceptions, Assertions Synopsis: try[-catch]-finally try { // fonctionnement normal … } catch (XXXException e) { // cas si XXXException se produit } finally { // le mot de la fin (dans tous les cas) } Bloc finally: Exécuté dans tous les cas • • qu’il y ait eu ou pas une exception Même s’il y avait un return dans le try et/ou catch Finally est exécuté • • Après le try, après le catch Avant de passer la main à la méthode appelante F. Mallet - POO 37 Exceptions, Assertions Finally vs. Try/catch finally est prioritaire sur ce qui se passe dans try et catch Si une exception est levée dans finally alors le return du try et du catch ne se produisent pas Si finally contient un return alors les exceptions propagées dans le try ou catch ne sont pas lancées Si finally ne fait ni return ni throw alors l’exception ou la valeur renvoyée dans le try/catch sont prises en compte ! Convention: Il faut éviter cette situation F. Mallet - POO 38 Exceptions, Assertions Automatic Resource Management Depuis le JDK 7 Enumération des ressources qui doivent être fermées automatiquement try ( Déclaration des ressources à fermer ) { // fonctionnement normal … } F. Mallet - POO 39 Exceptions, Assertions ARM: avant import java.io.*; public class Lecture { static public void main(String[] args) { try { FileReader fr = new FileReader("fic.txt"); BufferedReader br = new BufferedReader(fr); String ligne = null; do { ligne = br.readLine(); if (ligne == null) break; System.out.println(ligne); } while(true); br.close(); } catch(Exception ex) { System.err.println("Erreur de lecture : "+ex); } } } Si une exception se produit la ressource br n’est pas fermée F. Mallet - POO 40 Exceptions, Assertions ARM: avant import java.io.*; public class Lecture { static public void main(String[] args) { try { FileReader fr = new FileReader("fic.txt"); BufferedReader br = new BufferedReader(fr); String ligne = null; do { ligne = br.readLine(); System.out.println(ligne); } while(ligne != null); } catch(Exception ex) { System.err.println("Erreur de lecture : "+ex); } finally { br.close(); } } } Est illégal car br n’est visible que dans le bloc try ! F. Mallet - POO 41 Exceptions, Assertions ARM: avant import java.io.*; public class Lecture { static public void main(String[] args) { FileReader fr = new FileReader("fic.txt"); BufferedReader br = new BufferedReader(fr); try { String ligne = null; do { ligne = br.readLine(); System.out.println(ligne); } while(ligne != null); } catch(Exception ex) { System.err.println("Erreur de lecture : "+ex); } finally { br.close(); } } } Est illégal car new FileReader peut lever une exception ! 2 blocs try ! F. Mallet - POO 42 Exceptions, Assertions ARM: après import java.io.*; public class Lecture { static public void main(String[] args) throws FileNotFoundException, IOException { try ( FileReader fr = new FileReader("fic.txt"); BufferedReader br = new BufferedReader(fr) ; ) { String ligne = null; do { ligne = br.readLine(); System.out.println(ligne); } while(ligne != null); } } } Le close est appelé automatiquement sur fr et br Pas si la ressource est déjà fermée ! F. Mallet - POO 43 Exceptions, Assertions ARM : après Les ressources déclarées font parties de la clause try Les exceptions levées sont après le catch qui suit try ( FileReader fr = new FileReader("fic.txt"); BufferedReader br = new BufferedReader(fr) ) { } catch (FileNotFoundException fnfe) { … } Mais la méthode close() est appelée quoi qu’il arrive D’une façon similaire au finally Les variables déclarées ici doivent être d’un type qui réalise l’interface java.lang.AutoCloseable F. Mallet - POO 44 Exceptions, Assertions java.io.Closeable - java.lang.AutoCloseable java.lang.AutoCloseable public interface AutoCloseable { // close doit être idempotent (+s close == 1 seul close) void close() throws Exception; } java.io.Closeable public interface Closeable extends AutoCloseable{ // pas nécessairement idempotent mais recommandé void close() throws IOException; } F. Mallet - POO 45