Programmation objet avancée
Les exceptions
Plan
● Principes.
● Classes d'exception.
● La « propagation » des exceptions.
● Définir une classe d'exception.
Principes
● Lorsqu'un cas d'erreur intervient dans une méthode :
– façon C : retour d'une valeur négative ou NULL,
– façon Java/C++ : génération d'un objet exception.
● Problème : « capter » cet objet quand il existe et sinon, continuer l'exécution.
● Solution : syntaxe spéciale try/catch.
try {
// instructions }
catch(ClasseException e) {
// traitement de l'objet exception e
Principes
Mise en place (minimale) :
● on englobe une suite de N instructions dans try{}
● certaines (<= N) vont potentiellement générer des instances de M (>= 1) classes d'exceptions différentes.
● après la fermeture du try, on met M blocs catch, permettant chacun de traiter un des types d'exception.
Principes
Exemple n°1 de try/catch
...ObjectInputStream ois = null;
Double d = null;
try {
ois = new ObjectInputStream(new FileInputStream("toto.txt"));
d = (Double)(ois.readObject());
}
catch(IOException e) {
System.out.println("pb accès fichier : "+e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("pb de classe : "+e.getMessage());
}System.out.println("après try/catch");
Principes
Fonctionnement :
● les instructions du bloc try sont exécutées jusqu'à la génération d'une exception ou sinon jusqu'à la fin du bloc.
● pas d'exception : l'exécution continue après le dernier bloc catch,
● exception générée :
– l'exécution saute jusqu'au catch associé au type d'exception,
– exécution des instructions du catch,
– l'exécution continue après le dernier bloc catch.
Principes
Retour sur l'exemple n°1 :
● cas sans aucune exception :
– entrée dans le try,
– exécution des 2 instructions,
– sortie du try : saut après les catch,
– affichage de : après try/catch.
Principes
Retour sur l'exemple n°1 :
● cas exception à la 1ère instruction :
– entrée dans le try,
– exécution instruction 1,
– par ex. fichier inexistant Þ génération IOException,
– saut immédiat au catch(IOException),
– exécution instructions du catch,
– saut après les catch,
– affichage de : après try/catch.
Principes
Remarques :
● exception générée par une instruction Þ les instructions suivantes ne seront jamais exécutées.
● si plusieurs catch, le premier rencontré associé au type d'exception est exécuté
● si un catch exécuté, les autres catch ne seront jamais exécutés Þ suite de catch similaire à une structure if … else if … else if …
Les classes d'exception
● 2 types :
– vérifiées (= checked) : une méthode qui génère ce type oblige le programmeur à mettre son appel dans un try/catch.
– non vérifiées (= unchecked) : pas de try/catch obligatoire.
● exception non vérifiées = exceptions difficilement prévisibles :
– accès à un objet indéfini (NullPointerException),
– erreur arithmétique (ArithmeticException),
Les classes d'exception
Exemple n°2 : exception non vérifiée
try { ...
d = (Double)(ois.readObject());
double dd = 5.0/d; // si d=0, génération ArithmeticException }
catch(IOException e) {
System.out.println("pb accès fichier : "+e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("pb de classe : "+e.getMessage());
}
System.out.println("après try/catch");
ArithmeticException = type non vérifé
Les classes d'exception
Exemple n°3 : oubli catch exception vérifiée
try { ...
d = (Double)(ois.readObject());
}catch(IOException e) {
System.out.println("pb accès fichier : "+e.getMessage());
}
System.out.println("après try/catch");
ClassNotFoundException = type vérifé
Þ erreur de compilation :
error: unreported exception ClassNotFoundException; must be caught or declared to be thrown
Créer une méthode lanceuse d'exceptions
Principes:
● Ajouter throws … dans l'entête de la méthode
● La liste après throws contient le nom des classes d'exception qui peuvent être générées par la méthode.
● Dans le corps de la méthodes, si un cas d'erreur survient, on utilise throw (sans le s terminal) sur une instance de la classe d'exception désirée.
Créer une méthode lanceuse d'exceptions
Example n°4: (sur une classe personnalisée de liste chaînée)
class LinkedList { Cell head;
int listSize;
...
public Cell replace(int index, Cell c) throws IndexOutOfBoundsException,NullPointerException {
if (c == null) throw new NullPointerException();
if ((index < 0)||(index >= listSize)) { throw new IndexOutOfboundException();
} ...
} ...
}
La propagation des exceptions
Exemple n°5 : propagation implicite d'except. non vérifiées
class A { ...
private B b;
...
public void methodeA() { b.methodeB();
} ...
}
class B { ...
public void methodeB() { int[] tab = new int[10];
//gén. d'une IndexOutOfBoundException tab[25] = 3;
} ...
}
class Exemple4 { ...
public static void main(String[] args){
A a = new A();
a.methodeA();
...
La propagation des exceptions
Remarques sur l'exemple n°5 :
● main() appelle methodeA() qui appelle methodeB().
● methodeB() génère IndexOutOfBoundException
● cette exception n'est pas captée par methodeB() Þ elle est propagée à la méthode qui l'a appelé, i.e. methodeA()
● methodeA() ne capte pas non plus l'exception Þ elle est propagée à la méthode qui l'a appelé, i.e. main()
● main() ne capte pas plus l'exception Þ la JVM met fin à l'exécution du programme.
La propagation des exceptions
Exemple n°6 : propagation explicite d'exception vérifiée
class A {
private B b;
...
public void methodeA() { try { b.methodeB();}
catch(IOException e) { ... } }
}
class B {
FileReader fr = null;
...
public void methodeB() throws IOException { //gén. possible d'une IOException
fr = new FileReader("toto.txt");
...
class Exemple5 { ...
public static void main(...){
A a = new A();
a.methodeA();
...
} }
ajout
La propagation des exceptions
Remarques sur l'exemple n°6 :
● normalement methodeB() devrait utiliser try/catch pour entourer new FileReader(...)
● l'ajout de throws IOException permet de contourner cette obligation mais …
● s'il y a exception, elle n'est pas captée par methodeB() Þ elle est propagée à la méthode qui l'a appelé, i.e.
methodeA()
● methodeA() capte l'exception Þ la propagation s'arrête là.
● Si methodeA() n'utilise pas try/catch Þ erreur compilation
La propagation des exceptions
Remarques sur l'exemple n°6 (suite):
● on pourrait ajouter throws IOException dans l'entête de methodeA() pour qu'elle propage l'exception à main().
● dans ce cas, c'est dans main() qu'il faudrait un try/catch, sinon erreur compilation.
Þ explicite = propagation choisie par le programmeur
La propagation des exceptions
Pour résumer :
● Capturer les exceptions non vérifiées = mauvaise idée. Il faut utiliser la propagation implicite pour que le programme plante Þ bugs classiques (objet null, mauvais index, …) plus facile à détecter.
● Utiliser la propagation explicite quand un objet ne peut traiter lui-même l'exception Þ analogie avec le traitement administratif d'un problème : “Si je n'ai pas la connaissance/le pouvoir pour résoudre le problème, je le relaie à mon chef”.
Créer des classes d'exception
Principes:
● Hériter de Exception ou une autre classe existante.
● Customiser le message associé à l'exception :
– le constructeur appelle le super-constructeur avec un message spécial.
– et/ou redéfinir la méthode getMessage(). Remarques:
● la classe peut avoir des attributs, initialisés à la construction,
Créer des classes d'exception
Exemple n°7: (sur une classe personnalisée de liste chaînée)
class ListAccessException extends IndexOutOfBoundException { int index;
LinkedList list;
public ListAccessException(LinkedList list, int index) { super("Invalid cell index ["+index+"]");
this.index = index;
this.list = list;
}
public String getMessage() { String msg;
if (index>=list.size()) msg="Index greater than list size";
else if (index < 0) msg="Negative cell index";
return msg;
}
Créer des classes d'exception
Exemple n°7 (suite):
class LinkedList { Cell head;
int listSize;
...
public Cell get(int index) throws ListAccessException { if ((index < 0)||(index >= listSize)) {
throw new ListAccessException(this,index);
} ...
} ...
}