f. voisin : introduction à java 1 introduction à java - les exceptions - frédéric voisin fiifo -...
TRANSCRIPT
F. Voisin : Introduction à Java 1
Introduction à Java
- les exceptions -
Frédéric VOISIN
FIIFO - « Remise à Niveau »
F. Voisin : Introduction à Java 2
Pourquoi des « exceptions » ?
Gérer des « situations exceptionnelles » Cas d’erreurs liées
à l’environnement (JVM) à des erreurs de programmation (référence nulle, index de tableaux) à une application/classe particulière (next() sur un itérateur épuisé)
… ou tout autre événement impliquant une rupture (partielle) du déroulement normal du programme
Mécanisme utile si on ne sait pas que faire à l’endroit où le problème est découvert (on ne peut
que le signaler à l’appelant, et autrement que textuellement !)
Si on ne sait pas forcément à quel niveau le problème pourra être traité (par défaut on doit propager l’alerte)
F. Voisin : Introduction à Java 3
Pourquoi des « exceptions » ? (suite)
Signaler explicitement des cas « exceptionnels » Eviter de procéder par « retour de valeur particulière » qui ne sont pas toujours
testées par l’appelant Eviter que les cas « erronés » soient ignorés : une exception non traitée
interrompt le programme Eviter d’obscurcir les cas « normaux » avec les situations « exceptionnelles »
(tester toute référence à null avant usage ? Conversions à partir de Object ? )
Autoriser des traitements de rattrapage arbitrairement complexes
Autoriser des traitements sélectifs : chaque méthode décide quels cas exceptionnels elle sait traiter et quels cas relèvent des méthodes appelantes.
F. Voisin : Introduction à Java 4
« Gérer » une exception ?
Signaler une situation exceptionnelle (« lever une exception »)throw instance d’exception ;
Se protéger vis-à-vis d’une situation exceptionnelle, si on est capable de la prendre en compte (rattraper une exception) !
try { liste d’instructions } // bloc protégé !
catch (ClasseException1 variable){ traitement correctif 1 }catch (ClasseException2 variable){ traitement correctif 2 }…
Propager (explicitement ou implicitement) une exception qu’on ne sait pas traiter complètement
Souvent on est amené à combiner traitement local + propagation à l’appelant (sous le même nom d’exception ou un autre !)
F. Voisin : Introduction à Java 5
Qu’est-ce qu’une (classe) d’exception ?
Une exception = une instance d’une classe membre d’une hiérarchie (extensible) de classes, de racine Throwable
Error, Exception, RunTimeException, IOException, …
Une classe d’exception est une classe « ordinaire », avec éventuellement des constructeurs, des attributs, des méthodes
Méthodes héritées de Throwable : getMessage() toString() printStackTrace() // historique de propagation fillInStackTrace()
+ une description du contexte d’exécution au moment de la levée de l’exception
Les règles de typage classiques (classe/sous-classes) s’appliquent ! On peut donc organiser ses exceptions en hiérarchie
F. Voisin : Introduction à Java 6
class java.lang.Throwable sous-classe directe de Object !
class java.lang.Error Difficilement rattrapables !
class java.lang.VirtualMachineError
class java.lang.OutOfMemoryError…
class java.lang.Exceptionclass java.lang.RuntimeExceptiontrop fréquentes pour qu’on oblige
class java.lang.ArithmeticException à les signaler à l’avance !
class java.lang.ClassCastException elles peuvent donc survenir partout…
class java.lang.IllegalArgumentException
class java.lang.IllegalThreadStateException
class java.lang.NumberFormatException
class java.lang.IndexOutOfBoundsException
class java.lang.ArrayIndexOutOfBoundsException
class java.lang.StringIndexOutOfBoundsException
class java.lang.NegativeArraySizeException
class java.lang.NullPointerException
class java.util.NoSuchElementException
…
class java.io.IOException (voir transparent suivant !)
Ici prochainement, vos exceptions !
F. Voisin : Introduction à Java 7
La sous-hiérarchie « IOException »
class java.lang.Exception class java.io.IOException // dans le paquetage java.io
class java.io.EOFException class java.io.FileNotFoundException class java.io.InterruptedIOException class java.io.ObjectStreamException
class java.io.InvalidClassException class java.io.InvalidObjectException class java.io.NotActiveException class java.io.NotSerializableException // à voir ultérieurement class java.io.OptionalDataException class java.io.StreamCorruptedException class java.io.WriteAbortedException
Pour gérer toute exception liée aux E/S il suffit de rattraper IOException
On peut traiter de façon spécifique des cas particuliers (ex. les exceptions
FileNotFoundException et EOFException).
F. Voisin : Introduction à Java 8
Spécification des exceptions
Une méthode doit déclarer explicitement les exceptions qu’elle est susceptible de lever :
public void f(…) throws E1, E2 { … } le corps de f ne peut lever que les exceptions de classe E1 ou E2
f doit se préoccuper des exceptions levées par les méthodes qu’elle appelle soit en les rattrapant soit en les propageant (et en le déclarant dans son en-tête)
L’appelant de f sait donc contre quelles exceptions se prémunir… Toute redéfinition de f dans les sous-classes devra respecter ce profil …mais
rien n’empêche de lever une exception plus spécifique (sous-classe)
Exemple: toute redéfinition de equals(Object O) ne peut lever que des instances (au sens large) de RuntimeException ou Error.
La règle s’applique aussi à la méthode main !
F. Voisin : Introduction à Java 9
Spécification des exceptions (exemple)
import java.util.Calendar;
public class RnDate {
public RnDate(int year, int quantieme) throws ErreurDate { … refuse RnDate(2003, 366) ou RnDate(2003, -1) !
}
// KO : l’exception n’est ni rattrapée, ni « déclarée »
// Rejeté, même si en pratique l’exception ne peut pas se produire !
public static RnDate aujourdhui() {
Calendar date = Calendar.getInstance();
return new RnDate(date.get(Calendar.YEAR),
date.get(Calendar.DAY_OF_YEAR));
}
}
F. Voisin : Introduction à Java 10
Spécification des exceptions (suite)
A titre dérogatoire, il est facultatif de spécifier la levée des exceptions des hiérarchies Error et RunTimeException
public void f(…) { …} ne peut donc lever/propager que des exceptions de classe Error ou
RunTimeException, difficilement rattrapables
public void f(…) throws Throwable { …} peut lever des exceptions arbitraires mais laisse une (trop) lourde charge à ses
appelants et est peu coopérative
F. Voisin : Introduction à Java 11
Levée, récupération et propagation d’exceptions
Une rupture du déroulement normal du programme… :
arrêt du déroulement normal du programme pour rechercher dynamiquement un traite-exception adapté
Ce traitement remplace le traitement normalement effectué, sans possibilité de retour…
Levée implicite par la JVM ou une classe existante:String[] tab = new String[0];
Integer I; … ; System.out.println(Integer.intValue(I));
new Arraylist().iterator().next();
Levée explicite par le code du programmeur:throw new MonException();
F. Voisin : Introduction à Java 12
try { f1(); … fN(); } // bloc protégé
catch (E1 v) { instructions } // des traites-exceptions (« handlers »)…
…
catch (Ep v) { instructions } // … en nombre quelconque
Les catch sont examinés dans l’ordre jusqu’à trouver le premier qui correspond (modulo le sous-typage). Le compilateur vérifie que tout catch est accessible.
Le traite-exception retenu remplace la fin du traitement du bloc try et continue à sa suite, avec les mêmes contraintes (type d’une valeur retournée, exceptions levées, etc.)
A vous de conserver suffisamment d’information sur le contexte pour traiter l’exception (quel appel fi() a provoqué l’exception ?)
Levée, récupération et propagation d’exceptions
F. Voisin : Introduction à Java 13
Récupération d’exceptions (suite)
Un catch peut lui-même lever une exceptionla même : ou une autre :
catch (Exception e) { catch (Exception e) {
traitement local traitement local
throw e; throw newMonException();
} }
mais une exception levée dans un catch n’est jamais rattrapable dans le même bloc try/catch.
Les try/catch peuvent englober d’autres blocs try/catch
En l’absence de catch adapté, propagation automatique de l’exception au bloc englobant (où la même recherche de bloc catch aura lieu)
F. Voisin : Introduction à Java 14
Récupération d’exceptions (fin)
Attention à la portée des try/catch :
f(Iterator it) { f(Iterator it) {
try { while (true)
while (true) try {
it.next(); it.next();
} catch (…) { … } } catch (…) { … }
} }
F. Voisin : Introduction à Java 15
Levée, récupération et propagation d’exceptions
Recherche, selon l’imbrication des try/catch, d’un traite-exception adapté Si on en trouve un : l’exécution continue à partir de ce point de la méthode Sinon, l’exécution de la méthode est interrompue et l’exception est signalée chez
l’appelant, au point d’appel…
On remonte donc dynamiquement la chaîne d’appels de méthodes jusqu’à éventuellement la méthode main initiale
Si l’exception n’est jamais rattrapée, il y a interruption du programme…
On ne rattrape pas si on ne sait pas quoi en faire…
On ne rattrape pas si c’est juste pour propager !
F. Voisin : Introduction à Java 16
Exceptions et constructeurs
Attention aux exceptions dans les constructeurs : Dans quel état laisse-t-on l’instance concernée (initialisation partielle) ?
Qu’est-ce que l’utilisateur pourra faire avec l’instance concernée ? Une exception levée dans un constructeur n’est jamais récupérable dans un
autre constructeurpublic RnDate(int year, int quant) throws ErreurDate { …
}
public RnDate() { this(year, today); // Comment la rattraper ?
} Même problème pour la liaison constructeur sous-classe/super-classe ! Le constructeur de la sous-classe peut avoir des exceptions différentes…
F. Voisin : Introduction à Java 17
La clause « finally »
try {
instructions
} catch (Exception1 e1) { …
} … … … // traites-exceptions optionnels
} catch (ExceptionN eN) {
} finally { instructions } // clause optionnelle
Rôle : garantir qu’un fragment de code est exécuté dans tous les cas, normaux ou avec des exceptions, rattrapées ou non !
Précautions d’usage : attention aux doubles traitements attention à ne pas lever d’exceptions dans le bloc finally !
F. Voisin : Introduction à Java 18
La clause « finally » : exemple
class MonException extends Exception {}
public class TestFinally { // adapté de B. Eckel: "Thinking in Java" public static void main(String[] args) {
int count = 0;
while (true)
try { if (count == 0) { count++; throw new MonException(); }
else if (count == 2)
throw new NullPointerException();
else { count++; System.out.println("Cas normal"); }
} catch (MonException e) {
System.out.println("Dans le traite-exception");
} finally { System.out.println("Dans le bloc finally");}
}
}
Question : quels sont les affichages réalisés ?
F. Voisin : Introduction à Java 19
La clause « finally » dans les boucles …
class MonException extends Exception {}
public class TestFinally2 { // adapte de B. Eckel: "Thinking in Java" public static void main(String[] args) {
int count = 0;
while (count < 10)
try { count++;
if (count % 2 == 0) continue;
else if (count == 7) break;
else System.out.println("Cas normal: " + count);
} finally { // Exécuté dans les 3 cas ... System.out.println("Dans le bloc finally: " + count);
}
}
}
F. Voisin : Introduction à Java 20
Eléments méthodologiques
On anticipe (i.e. on évite l’exception par un test explicite) ou on rattrape l’exception ?
Privilégier la correction du code et sa lisibilité
Une partie intégrante de la conception d’une classe
Qui lève quoi ? Aussi important que les types des paramètres !
Quel niveau de détail :
quelle interface (attributs, méthodes) ?
Quelle hiérarchie dans les exceptions ?
F. Voisin : Introduction à Java 21
Eléments méthodologiques : qui rattrape quoi ?
Idéalement : on ne rattrape une exception que si on sait quoi en faire. Halte aux rattrapages abusifs et aux messages d’erreur intempestifs !
Exemple: une pile d’éléments de capacité bornée est pleine.
L’utilisateur essaie d’empiler un nouvel élément. Vous faites quoi ?
En phase de mise au point : il vaut mieux ne pas rattraper les exceptions qui ne devraient pas se produire (=> facilite la mise au point grâce à la trace de propagation)
En production: on peut préférer un code plus « robuste » : on rattrape, à condition de savoir comment repartir du bon pied !
F. Voisin : Introduction à Java 22
Eléments méthodologiques : qui rattrape quoi ? (suite)
Il est courant de rattraper une exception pour en lever une autre à la place (éventuellement après un traitement local)
public RendezVous(RnDate d, int q, String s, Person p) throws ErreurRendezVous {
if (q < 1) throw new ErreurRendezVous ("erreurDurée");
try { this.deb = d; this.raison = s, this.qui = p; this.fin = new RnDate(d.annee(), d.quantieme() +
q);// OK ? } catch (ErreurDate e) {
p.prevenir("pas pendant le reveillon"); throw new ErreurRendezVous ("erreurAnnée");
}
Il reste des éléments de choix : une ou plusieurs exception pour les rendez-vous ? Quel rapport hiérarchique entre les exceptions des rendez-vous, intervalles et dates ?
F. Voisin : Introduction à Java 23
Eléments méthodologiques (fin)
Qu’est-ce qu’un cas exceptionnel ?
Exemple: la méthode add(Object O) de l’interface Collection : que se passe-t-il si on ajoute un élément ?
Certaines collections admettent les doubles (ArrayList) D’autres collections n’ont jamais de doubles (TreeSet) Certaines collections ont des contraintes supplémentaires
TreeSet : ne traite que les éléments munis d’une (même) relation d’ordre d’autres refusent d’ajouter null. D’autres enfin refusent d’implémenter add (ou remove)
Toutes doivent être vues comme des sous-classes de Collection. Leurs versions de add doit se faire à profil d’exception constant.
Quelles exceptions prévoyez-vous ? Pour quels cas de figure ?
F. Voisin : Introduction à Java 24
Eléments méthodologiques : Exemple (fin)
Un garde-fou à garantir :
après C.add(e), si l’appel de méthode s’est bien passé, e doit être dans C !
Il n’est pas « exceptionnel » d’ajouter un élément à une collection qui le contient : add renvoie true si l’ajout est possible et C n’a pas été modifiée add renvoie false si l’ajout est possible et C n’a pas été modifiée add lève une exception si l’ajout est impossible :
UnsupportedOperationException pour celles qui n’implémentent pas add
ClassCastException pour celles qui ont des contraintes « de type »
IllegalArgumentException pour d’autres contraintes sur les éléments
Ces trois classes d’exception sont classées dans RuntimeException pour ne pas avoir à être spécifiées explicitement.
On peut donc jouer sur la valeur de retour et/ou sur la levée d’exception !