Gestion des erreurs Gestion des erreurs (2)
Les exceptions permettent d’anticiper les erreurs qui pourront potentiellement
se produire lors de l’utilisation d’une portion de code. À retourner une valeur choisie à l’avance :
Exemple : on veut écrire une fonction qui calcule l’inverse d’un nombre réel double f(double x) {
quand c’est possible : if (x != 0.0) {
return 1.0 / x;
f
} else {
entrée : x return Double.MAX_VALUE;
sortie : 1/x }
}
Si x = 0
erreur Mais cela
Sinon 1. n’indique pas à l’utilisateur potentiel qu’il a fait une erreur
retourner 1/x
2. retourne de toutes façons un résultat inexact ...
3. suppose une convention arbitraire (la valeur à retourner en cas d’erreur)
+ Mais que faire concrètement en cas d’erreur ?
Gestion des erreurs (3) Gestion des erreurs (4)
 retourner un code d’erreur :
Á afficher un message d’erreur : boolean f(double x, Double resultat) {
double f(double x) { if (x != 0.0) {
if (x != 0.0) { resultat = 1.0 / x;
return 1.0 / x; return true;
} else { } else {
[Link]("Erreur dans f : division par 0"); return false;
return ????; }
} }
}
Cette solution est déjà meilleure car elle laisse à la fonction qui appelle f le
mais que retourner effectivement en cas d’erreur ?... soin de décider quoi faire en cas d’erreur.
Cela présente néanmoins l’inconvénient d’être assez lourd à gérer pour finir :
...on retombe en partie sur le cas précédent
I cas de l’appel d’appel d’appel.... ...d’appel de fonction,
De plus, cela est très mauvais car produit des effets de bord : affichage dans I mais aussi écriture peu intuitive :
le terminal alors que ce n’est pas du tout dans le rôle de f ! if (f(x,y))... // le résultat de la division est dans y
au lieu de
y=f(x);
Exceptions Exceptions (2)
Principe :
I lorsque qu’une erreur a été détectée à un endroit, on la signale en « lançant »
Il existe une solution permettant de généraliser et d’assouplir cette dernière
un objet contenant toutes les informations que l’on souhaite donner sur l’erreur
solution : déclencher une exception
(« lancer » = créer un objet disponible pour le reste du programme)
+ mécanisme permettant de prévoir une erreur à un endroit I à l’endroit où l’on souhaite gérer l’erreur (au moins partiellement), on peut
et de la gérer à un autre endroit « attraper » l’objet « lancé » (« attraper » = utiliser)
I si un objet « lancé » n’est pas attrapé du tout, cela provoque l’arrêt du
programme : toute erreur non gérée provoque l’arrêt.
Un tel mécanisme s’appelle « gestion des exceptions ».
Syntaxe de la gestion des exceptions Syntaxe de la gestion des exceptions
On cherche à remplir 4 tâches élémentaires :
1. signaler une erreur
2. marquer les endroits réceptifs aux erreurs
3. leur associer (à chaque endroit réceptif) un moyen de gérer les erreurs qui se Notez bien que :
présentent I L’indication des erreurs (throw) et leur gestion (try/catch)
4. éventuellement, « faire le ménage » après un bloc réceptif aux erreurs sont le plus souvent à des endroits bien séparés dans le code
I Chaque bloc try possède son/ses catch associé(s)
On a donc 4 mots du langage Java dédiés à la gestion des exceptions :
throw : indique l’erreur (i.e. « lance » l’exception)
try : indique un bloc réceptif aux erreurs
catch : gère les erreurs associées (i.e. les « attrape » pour les traiter)
finally : (optionel) indique ce qu’il faut faire après un bloc réceptif
Pour résumer Exceptions : intérêt
Une exception est un moyen de signaler un événement nécéssitant une attention Avantages de la gestion des exceptions par rapports aux codes d’erreurs retournés
spéciale au sein d’un programme, comme: par des fonctions :
I écriture plus facile, plus intuitive et plus lisible
I une erreur grave I propagation automatique de l’exception aux niveaux supérieurs d’appel
I une situation inhabituelle devant être traitées de façon particulière (fonction appelant une fonction appelant ...)
plus besoin de gérer obligatoirement l’erreur au niveau de la fonction appelante
+ But : améliorer la robustesse des programmes en : I une erreur peut donc se produire à n’importe quel niveau d’appel, elle sera
toujours reportée par le mécanisme de gestion des exceptions
I séparant le code de traitement des erreurs du code « effectif »
Note : si une erreur peut être gérée localement, le faire et ne pas utiliser le
I fournissant le moyen de forcer une réponse à des erreurs particulières mécanisme des exceptions.)
Syntaxe de la gestion des exceptions throw
On cherche à remplir 4 tâches élémentaires :
1. signaler une erreur throw est l’instruction qui signale l’erreur au reste du programme.
2. marquer les endroits réceptifs aux erreurs Syntaxe : throw exception
3. leur associer (à chaque endroit réceptif) un moyen de gérer les erreurs qui se exception est un objet de type Exception qui est « lancé »
présentent au reste du programme pour être « attrapé »
4. éventuellement, « faire le ménage » après un bloc réceptif aux erreurs
Exemple :
On a donc 4 mots du langage Java dédiés à la gestion des exceptions : throw new Exception("Quelle erreur !");
throw : indique l’erreur (i.e. « lance » l’exception)
try : indique un bloc réceptif aux erreurs Exception est une classe de [Link] qui possède de nombreuses sous-classes
catch : gère les erreurs associées (i.e. les « attrape » pour les traiter) et qui hérite de Throwable.
finally : (optionel) indique ce qu’il faut faire après un bloc réceptif
throw (2) Hiérarchie partielle de Throwable
throw, en « lançant » une exception, interrompt le cours normal d’exécution et :
I saute au bloc catch du bloc try directement supérieur, si il existe ; Throwable
I quitte le programme si l’exécution courante n’était pas dans au moins
un bloc try. Error Exception
Exemple : ...
... VirtualMachineError
RuntimeException IOException
try {
// ... ...
if (...) { OutOfMemoryError
throw new Exception("Quelle erreur !"); IndexOutOfBoundException IllegalArgumentException
}
// ...
}
catch (Exception e) { ArrayIndexOutOfBoundException NumberFormatException
// ...
}
Les sous-classes de Throwable La classe [Link]
Chaque sous-classe de Throwable décrit une erreur précise
public class Throwable extends Object
La classe Error : 12 sous-classes directes
I Erreur fatale I Deux constructeurs:
I Pas censée être traitée par le programmeur I Erreur avec ou sans message
public Throwable()
La classe Exception : 74 sous-classes directes (hormis les RuntimeException)
public Throwable(String message)
I Circonstance exceptionnelle
I Souvent une erreur (mais pas toujours) I deux méthodes (parmi d’autres) :
I Doit être traitée par le programmeur (« checked exceptions ») I Accès au message d’erreur
I Affichage du chemin vers l’erreur
La classe RuntimeException : 49 sous-classes directes
public String getMessage()
I Exception dont le traitement n’est pas vérifié par le compilateur public void printStackTrace()
I Peut être traitée par le programmeur (« unchecked exceptions »)
try catch
catch est le mot-clé introduisant un bloc dédié à la gestion d’une ou plusieurs
exceptions.
try (lit. « essaye ») introduit un bloc réceptif aux exceptions lancées par des
instructions, ou des méthodes appelées à l’intérieur de ce bloc (ou même des Tout bloc try doit toujours être suivi d’au moins un bloc catch gérant les exceptions
méthodes appelées par des méthodes appelées par des méthodes... ... pouvant être lancées dans ce bloc try.
... à l’intérieur de ce bloc)
Si une exception est lancée mais n’est pas interceptée par le catch correspondant,
Exemple : le programme s’arrête (message « Exception in thread ... » et affichage de la
trace d’exécution).
try {
// ...
y = f(x); // f pouvant lancer une exception Syntaxe :
// ...
catch (type nom) { ... }
}
intercepte toutes les exceptions de type type lancées depuis le bloc try précédent
type peut-être une classe prédéfinie de la hiérarchie d’exceptions de Java ou une
classe d’exception créée par le programmeur.
Exemple d’utilisation de catch Flot d’exécution (1/3)
try {
// ...
if (age >= 150)
{ throw new Exception("age trop grand"); } Un bloc catch n’est exécuté que si une exception de type correspondant a été
// ... lancée depuis le bloc try correspondant.
if (x == 0.0)
{ throw new ArithmeticException("Division par zero"); } Sinon le bloc catch est simplement ignoré.
// ...
}
En l’absence du bloc finally, si un bloc catch est exécuté, le déroulement
catch (ArithmeticException e) { continue ensuite normalement après ce bloc catch
[Link]([Link]()); (ou après le dernier des blocs catch du même bloc try, lorsqu’il y en a plusieurs).
[Link]();
} En aucun cas l’exécution ne reprend après le throw !
catch (Exception e) {
[Link]("Qui peut vivre si vieux ?");
}
Flot d’exécution (2/3) Flot d’exécution (3/3)
Exemple : Exemple :
en cas d’erreur (lancement d’une exception) : si il n’y a pas d’erreur (pas de lancement d’exception) :
try { try {
// ... // ...
if (...) { if (...) {
throw new Exception("Quelle erreur !"); throw new Exception("Quelle erreur !");
} }
// ... // ...
} }
catch (Exception e) { catch (Exception e) {
// ... // ...
} }
try/throw/catch dans la même méthode catch : remarques
int lireEntier(int maxEssais) throws Exception
{
int nbEssais = 1;
do {
[Link]("Donnez un entier : ");
try { Notes :
int i = [Link](); I Java 7 a introduit le multi-catch : catch(Exception1 | Exception2 | ..)
return i;
}
I s’il y a plusieurs blocs catchs toujours les spécifier du plus spécifique au plus
catch (InputMismatchException e) { général
[Link]("Il faut un nombre entier. Recommencez !"); (sinon, erreur signalée par le compilateur)
[Link]();
++nbEssais;
}
} while(nbEssais <= maxEssais);
throw new Exception ("Saisie échouée");
}
Le bloc finally Bloc finally : exemple (1)
class Inverse {
public static void main (String[] args) {
try {
int b = [Link](args[0]);
Le bloc finally est optionnel, il suit les blocs catch int c = 100/b;
[Link]("Inverse * 100 = " + c);
}
Il contient du code destiné à être exécuté qu’une exception ait été lancée ou pas catch (NumberFormatException e1) {
par le bloc try [Link]("Il faut un nombre entier !");
}
catch (ArithmeticException e2) {
+ But : faire le ménage (fermer des fichiers, des connexions etc..) [Link] ("Parti vers l'infini !");
}
finally {
[Link]("Passage obligé !");
}
}
}
Bloc finally : exemple (2)
Exemples d’exécution :
>java Inverse 4.1
Il faut un nombre entier!
Passage obligé !
>java Inverse 0
Parti vers l’infini!
Passage obligé !
>java Inverse 4
Inverse * 100 = 25
Passage obligé !
>java Inverse
Passage obligé !
Exception in thread "main" [Link]: 0
« Relancement » « Relancement » : exemple
catch (Exception erreur) {
Une exception peut être partiellement traitée par un bloc catch et attendre un // traitement partiel :
traitement plus complet ultérieur (c’est-à-dire à un niveau supérieur). [Link]("Hmm... pour l'instant "
+ "je ne sais pas quoi faire"
Il suffit pour cela de « relancer » l’exception au niveau du bloc n’effectuant que le + "avec l'erreur :"
traitement partiel. + [Link]());
(Il faudra bien sûr pour cela que l’appel à ce bloc catch soit lui-même dans un autre bloc throw erreur; // relance l'exception captée
// throw new Exception("un autre message"); // alternative
try à un niveau supérieur).
}
Déclaration d’une exception Règle « déclarer ou traiter »
Une méthode lançant une exception sans la traiter localement doit généralement
informer qu’elle le fait
Toutes les exceptions en dehors des RunTimeException et des Error doivent :
Ceci se fait en ajoutant une clause throws à l’entête de la méthode
Syntaxe: I soit être interceptées dans la méthode où elles sont lancées;
I soit être déclarées par la méthode.
Type method(...) throws Exception1, Exception2, ...
Exemple: Si une exception de ce type est lancée sans être interceptée
+ le compilateur émettra un message d’erreur
int inverse(int x) throws Exception
{ + « Checked exceptions »
if (x == 0) {
throw new Exception("Une division par zéro");
}
return 1/x;
}
Exceptions personnalisées Exceptions personnalisées
Il est possible de programmer ses propres classes d’exception
+ sous-classe de Exception (ou d’une autre sous-classe d’exception existante)
Il et possible de définir dans une sous-classe d’exception personnalisée tout
Contenu minimal : membre jugé utile :
class MonException extends Exception I code d’erreurs
{
public MonException() {
I informations sur le contexte de détection de l’exception
super("mon message par défaut";); I etc.
}
public MonException(String message) {
super(message); Exemple . . .
}
}
+ permet de préserver le comportement attendu de getMessage()
Exceptions personnalisées : exemple Exceptions personnalisées : exemple (2)
class TropChaudException extends Exception try {
{ //...
private double temperatureAnormale; if (temperature > TEMP_MAX){
private String consigne; throw new TropChaudException(temperature,
"Vérification de l'appareil de mesure");
public TropChaudException() { super("Température trop élevée"); } }
public TropChaudException(String message) { super(message); } }
public TropChaudException(double uneTemperature, String uneConsigne) { catch(TropChaudException e){
super("Température trop élevée"); [Link]([Link]() + " : " );
temperatureAnormale = uneTemperature; [Link]([Link]());
consigne = uneConsigne; [Link]("Consigne -> " + [Link]());
} }
public double getTemperature() {
return temperatureAnormale;
} Exemple d’exécution du bloc catch :
public String getConsigne() {
return consigne; Température trop élevée : 150.0
} Consigne -> Vérification de l’appareil de mesure
}
Exemple complet (1) Exemple complet (2)
public static void main(String[] args) {
int nbEssais = 0; catch (ArithmeticException e) {
final int MAX_ESSAIS = 2; if (nbEssais < MAX_ESSAIS) {
ArrayList<Double> mesures = new ArrayList<Double>(); [Link]("Ressaisir les valeurs !");
} else {
do { [Link]("Il y a déjà eu au moins "
nbEssais++; + MAX_ESSAIS + " essais.");
[Link](" -> abandon");
acquerirTemp(mesures); // remplit le tableau }
}
try { } while (nbEssais < MAX_ESSAIS);
plotTempInverse(mesures); }
}
// ...
Exemple complet (3) Exemple complet (4)
private static void plotTempInverse(ArrayList<Double> t) private static void plot(double x) {
throws ArithmeticException // fait le dessin
{ }
for(int i = 0; i < [Link](); i++) {
try { private static double inverse(double x)
plot(inverse([Link](i))); throws ArithmeticException // PAS NECESSAIRE
} catch (ArithmeticException e) { //RunTimeException
[Link]("Problème à l'indice :" + i); {
// RELANCEMENT if (x == 0.0) {
throw e; throw new ArithmeticException("Division par 0 !");
} }
} return 1.0/x;
} }
Conseils/mise en garde
La gestion d’une exception coûte beaucoup plus en temps de calcul qu’un simple
if.. then.. else
+ Si l’erreur peut-être traitée là où elle est découverte,
il faut le faire sans passer par les exceptions
Lancer des exceptions spécifiques est plus informatif et utile !