1. Les exceptions en Java : syntaxe, rôle, classes
2.1 Enoncé
2.2 La démarche
2.3 Un programme Java solution
- les classes d'exception
- les blocs lançant les exceptions
- les blocs interceptant les exceptions
- le bloc <AfficherFichier>
- le bloc <ActionsSurFichier>
- le programme Java
- les exécutions du progarmme
1. Les exceptions en Java : syntaxe, rôle, classes
1.1 Définition et rappels
Rappellons au lecteur que nous avons déjà vu les base sur les exceptions avec Delphi au chapitre 5.9 du package, la sécurité d'une application peut être rendue instable par toute une série de facteurs :
Des problèmes liés au matériel : par exemple la perte subite d'une connection à un port, un disque défectueux... Des actions imprévues de l'utilisateur, entrainant par exemple une division par zéro... Des débordements de stockage dans les structures de données...
Toutefois les faiblesses dans un logiciel pendant son exécution, peuvent survenir : lors des entrées-sorties, lors de calculs mathématiques interdits (comme la division par zéro), lors de fausses manoeuvres de la part de l’utilisateur, ou encore lorsque la connexion à un périphérique est inopinément interrompue, lors d'actions sur les données. Le logiciel doit donc se " défendre " contre de tels incidents potentiels, nous nommerons cette démarche la programmation défensive !
Progammation défensive
En programmation défensive il est possible de protéger directement le code à l’aide de la notion d’exception. L’objectif principal est d’améliorer la qualité de " robustesse " (définie par B.Meyer) d’un logiciel. L’utilisation des exceptions avec leur mécanisme intégré, autorise la construction rapide et efficace de logiciels robustes.
La programmation défensive est une attitude de pensée consistant à prévoir que le logiciel sera soumis à des défaillances dues à certains paramètres externes ou internes et donc à prévoir une réponse adaptée à chaque type de situation.
Rôle d’une exception
Une exception est chargée de signaler un comportement exceptionnel (mais prévu) d’une partie spécifique d’un logiciel. Dans les langages de programmation actuels, les exceptions font partie du langage lui-même. C’est le cas de Java qui intègre les exceptions comme une classe particulière : la classe Exception. Cette classe contient un nombre important de classes dérivées.
Comment agit une exception
Dès qu’une erreur se produit comme un manque de mémoire , un calcul impossible, un fichier inexistant, un transtypage non valide,..., un objet de la classe adéquate dérivée de la classe Exception est instancié. Nous dirons que le logiciel " déclenche une exception ".
1.2 Comment gérer une exception dans un programme
Programme sans gestion de l'exception
Soit un programme Java contenant un incident d'exécution (une division par zéro dans l'instruction x = 1/0; ) dans la méthode meth() de la classe Action1, cette méthode est appelée dans la classe UseAction1 à travers un objet de classe Action1 :
Lors de l'exécution, après avoir affiché les chaînes "Début du programme" et " ...Avant incident", le programme s'arrête et la java machine signale une erreur. Voici ci-dessous l'affichage obtenu sur la console lors de l'exécution :
---- java UseAction1
Début du programme
...Avant incident
java.lang.ArithmeticException : / by zero
---- : Exception in thread "main"
Que s'est-il passé ?
La méthode main :
- a instancié un objet Obj de classe Action1,;
- a affiché sur la console la phrase "Début du programme",
- a invoqué la méthode meth() de l'objet Obj,
- a affiché sur la console la phrase " ...Avant incident",
- a exécuté l'instruction "x = 1/0;"
Dès que l'instruction "x = 1/0;" a été exécutée celle-ci a provoqué un incident. En fait une exception de la classe ArithmeticException a été "levée" (un objet de cette classe a été instancié) par la Java machine.
La Java machine a arrêté le programme immédiatement à cet endroit parce qu'elle n'a pas trouvé de code d'interception de cette exception qui a été automatiquement levée :
Nous allons voir comment intercepter (on dit aussi "attraper" - to catch) cette exception afin de faire réagir notre programme afin qu'il ne s'arrête pas brutalement.
Programme avec gestion de l'exception
Java possède une instruction de gestion des exceptions, qui permet d'intercepter des exceptions dérivant de la classe Exception :
try ... catch
Syntaxe minimale d'un tel gestionnaire :
try { <lignes de code à protéger>
}catch ( UneException E ) {<lignes de code réagissant à l’exception UneException >
}
Le type UneException est obligatoirement une classe qui hérite de la classe Exception.
Schéma du fonctionnement d'un tel gestionnaire :
Le gestionnaire d'exception "déroute" l'exécution du programme vers le bloc d'interception catch qui traite l'exception (exécute le code contenu dans le bloc catch), puis renvoie et continue l'exécution du programme vers le code situé après le gestionnaire lui-même.
Principe de fonctionnement de l'interception
Dès qu'une exception est levée (instanciée), la Java machine stoppe immédiatement l'exécution normale du programme à la recherche d'un gestionnaire d'exception susceptible d'intercepter (saisir) et traiter cette exception. Cette recherche s'effectue à partir du bloc englobant et se poursuit sur les blocs plus englobants si aucun gestionnaire de cette exception n'a été trouvé.
Soit le même programme Java que précédemment, contenant un incident d'exécution (une division par zéro dans l'instruction x = 1/0; ). Cette fois nous allons gérer l'incident grâce à un gestionnaire d'exception try..catch dans le bloc englobant immédiatement supérieur.
Programme sans traitement de l'incident :
Programme avec traitement de l'incident par try...catch :
Ci-dessous l'affichage obtenu sur la console lors de l'exécution de ce programme :
---- java UseAction1
Début du programme.
...Avant incident
Interception exception
Fin du programme.
---- : operation complete.
- Nous remarquons donc que la Java machine a donc bien exécuté le code d'interception situé dans le corps du "catch( ArithmeticException E ){...}" et a poursuivi l'exécution normale après le gestionnaire.
- Le gestionnaire d'exception se situe dans la méthode main (code englobant) qui appelle la méthode meth() qui lève l'exception.
1.3 Interception d'exceptions hiérarchisées
Interceptions de plusieurs exceptions
Dans un gestionnaire try...catch, il est en fait possible d'intercepter plusieurs types d'exceptions différentes et de les traiter.
Ci-après nous montrons la syntaxe d'un tel gestionnaire qui fonctionne comme un selecteur ordonné, ce qui signifie qu'une seule clause d'interception est exécutée.
Dès qu'une exception intervient dans le < bloc de code à protéger>, la Java machine scrute séquentiellement toutes les clauses catch de la première jusqu'à la nième. Si l'exception actuellement levée est d'un des types présents dans la liste des clauses le traitement associé est effectué,la scrutation est abandonnée et le programme poursuit son exécution après le gestionnaire.
Syntaxe :
try {
< bloc de code à protéger>
}
catch ( TypeException1 E ) { <Traitement TypeException1 > }
catch ( TypeException2 E ) { <Traitement TypeException2 > }
.....
catch ( TypeExceptionk E ) { <Traitement TypeExceptionk > }
Où TypeException1, TypeException12, ... , TypeExceptionk sont des classes d'exceptions obligatoirement toutes distinctes.
Seule une seule clause catch ( TypeException E ) {...}est exécutée (celle qui correspond au bon type de l'objet d'exception instancié).
Exemple théorique, supposons que la méthode meth() de la classe Action2 puisse lever trois types différents d'exceptions: ArithmeticException, ArrayStoreException, ClassCastException.
Notre gestionnaire d'exceptions est programmé pour intercepter l'une de ces 3 catégories. Nous figurons ci-dessous les trois schémas d'exécution correspondant chacun à la levée (l'instanciation d'un objet) d'une exception de l'un des trois types et son interception :
Interception d'une ArrayStoreException :
Interception d'une ClassCastException :
Interception d'une ArithmeticException :
1.4 Ordre d'interception d'exceptions hiérarchisées
Dans un gestionnaire try...catch comprenant plusieurs clauses, la recherche de la clause catch contenant le traitement de la classe d'exception appropriée, s’effectue séquentiellement dans l’ordre d’écriture des lignes de code.
Soit le pseudo-code java suivant :
try {
< bloc de code à protéger générant un objet exception>
}
catch ( TypeException1 E ) { <Traitement TypeException1 > }
catch ( TypeException2 E ) { <Traitement TypeException2 > }
.....
catch ( TypeExceptionk E ) { <Traitement TypeExceptionk > }
La recherche va s'effectuer comme si le programme contenait des if...else if... imbriqués. Les tests seraient effectués sur l'appartenance de l'objet d'exception à une classe à l'aide de l'opérateur instanceof :
if (<Objet exception> instanceof TypeException1) { <Traitement TypeException1 > }
else if (<Objet exception> instanceof TypeException2) { <Traitement TypeException2 > }
...
else if (<Objet exception> instanceof TypeExceptionk) { <Traitement TypeExceptionk > }
Rappellons que l'opérateur instanceof agit sur une classe et ses classes filles (sur une hérarchie de classes), c'est à dire que tout objet de classe TypeExceptionX est aussi considéré comme un objet de classe parent au sens du test d'appartenance en particulier cet objet de classe TypeExceptionX est aussi considéré objet de classe Exception qui est la classe mère de toutes les exceptions Java.
Le test d'appartenance de classe dans la recherche d'une clause catch fonctionne d'une façon identique à l'opérateur instanceof dans les if...else
On choisira donc, lorsqu’il y a une hiérarchie entre les exceptions à intercepter, de placer le code de leurs gestionnaires dans l’ordre inverse de la hiérarchie.
Exemple : Soit une hiérarchie d'exceptions dans java
java.lang.Exception
|
+--java.lang.RuntimeException
|
+--java.lang.ArithmeticException
|
+--java.lang.ArrayStoreException
|
+--java.lang.ClassCastException
Soit le modèle de gestionnaire d'interception déjà fourni plus haut :
try {
< bloc de code à protéger générant un objet exception>
}
catch ( ArithmeticException E ) { <Traitement ArithmeticException> }
catch ( ArrayStoreException E ) { <Traitement ArrayStoreException> }
catch ( ClassCastException E ) { <Traitement ClassCastException> }
Supposons que nous souhaitions intercepter une quatrième classe d'exception, par exemple une RuntimeException, nous devons rajouter une clause :
catch ( RuntimeException E ) { <Traitement RuntimeException>}
Insérons cette clause en premier dans la liste des clauses d'interception :
Nous lançons ensuite la compilation de cette classe et nous obtenons un message d'erreur :
Résultats de l'exécution :
---- java UseAction2
UseAction2.java:19: exception java.lang.ArithmeticException has already been caught
catch(ArithmeticException E){
^
UseAction2.java:22: exception java.lang.ArrayStoreException has already been caught
catch(ArrayStoreException E){
^
UseAction2.java:25: exception java.lang.ClassCastException has already been caught
catch(ClassCastException E){
^
3 errors
Le compilateur proteste à partir de la clause catch ( ArithmeticException E )en nous indiquant que l'exception est déjà interceptée et ceci trois fois de suite.
Que s'est-il passé ?
Le fait de placer en premier la clause catch ( RuntimeException E ) chargée d'intercepter les exceptions de classe RuntimeException implique que n'importe quelle exception héritant de RuntimeException comme par exemple ArithmeticException, est considérée comme une RuntimeException. Dans un tel cas cette ArithmeticException est interceptée par la clause catch ( RuntimeException E ) mais elle n'est jamais interceptée par la clause catch ( ArithmeticException E ).
Le seul endroit où le compilateur Java acceptera l'écriture de la clause catch ( RuntimeException E ) se situe à la fin de la liste des clauses catch. Ci-dessous l'écriture d'un programme correct :
Dans ce cas la recherche séquentielle dans les clauses permettra le filtrage correct des classes filles puis ensuite le filtrage des classes mères.
On choisira donc, lorsqu’il y a une hiérarchie entre les exceptions à intercepter, de placer le code de leurs clauses dans l’ordre inverse de la hiérarchie.
1.5 Redéclenchement d'une exception : throw
Il est possible de déclencher soi-même des exceptions en utilisant l'instruction throw, voir même de déclencher des exceptions personnalisées ou non.
Déclenchement manuel d'une exception existante
La Java machine peut déclencher une exception automatiquement comme dans l'exemple de la levée d'une ArithmeticException lors de l'exécution de l'instruction "x = 1/0 ;".
La Java machine peut aussi lever (déclencher) une exception à votre demande suite à la rencontre d'une instruction throw. Le programme qui suit lance une ArithmeticException avec le message "Mauvais calcul !" dans la méthode meth() et intercepte cette exception dans le bloc englobant main. Le traitement de cette exception consiste à afficher le contenu du champ message de l'exception grâce à la méthode getMessage() :
Résultats de l'exécution :
---- java UseAction3
Début du programme.
...Avant incident
Interception exception : Mauvais calcul !
Fin du programme.
---- : operation complete.
Déclenchement manuel d'une exception personnalisée
Pour une exception personnalisée, le mode d'action est strictement identique, il vous faut seulement auparavant créer une nouvelle classe héritant obligatoirement de la classe Exception ou de n'importe laquelle de ses sous-classes.
Reprenons le programme précédent et créons une classe d'exception que nous nommerons ArithmeticExceptionPerso héritant de la classe des ArithmeticException puis exécutons ce programme :
Résultats de l'exécution :
---- java UseAction3
Début du programme.
...Avant incident
Interception exception : Mauvais calcul !
Fin du programme.
---- : operation complete.
L'exécution de ce programme est identique à celle du programme précédent, notre exception fonctionne bien comme celle de Java.
1.6 Exception vérifiée ou non
La majorité des exceptions de Java font partie du package java.lang. Certaines exceptions sont tellement courantes qu'elles ont été rangées par Sun (concepteur de Java) dans une catégorie dénommée la catégorie des exceptions implicites ou non vérifiées (unchecked), les autres sont dénommées exception explicites ou vérifiées (checked) selon les auteurs.
Une exception non vérifiée (implicite) est une classe dérivant de l'une des deux classes Error ou RuntimeException :
java.lang.Object
|
+--java.lang.Throwable
|
+--java.lang.Error
|
+--java.lang.Exception
|
+--java.lang.RuntimeException
Toutes les exceptions vérifiées ou non fonctionnent de la même manière, la différence se localise dans la syntaxe à adopter dans une méthode propageant l'un ou l'autre genre d'exception.
Nous pouvons déjà dire que pour les exceptions non vérifiées, il n'y a aucune contrainte syntaxique pour propager une exception d'une méthode vers un futur bloc englobant. La meilleure preuve de notre affirmation est qu'en fait nous n'avons utilisé jusqu'ici que des exceptions non vérifiées, plus précisément des exceptions dérivant de RuntimeException et que nous n'avons utilisé aucune syntaxe spéciale dans la méthode meth() pour indiquer qu'elle était susceptible de lever une exception :
+--java.lang.RuntimeException
|
+--java.lang.ArithmeticException
|
+--java.lang.ArrayStoreException
|
+--java.lang.ClassCastException
Il n'en est pas de même lorsque l'exception lancée (levée) dans la méthode meth() est une exception vérifiée. Le paragraphe suivant vous explique comment agir dans ce cas.
1.7 Méthode propageant une exception vérifiée : throw
Une méthode dans laquelle est levée une ou plusieurs exceptions vérifiées doit obligatoirement signaler au compilateur quelles sont les classes d'exceptions qu'elle laisse se propager sans traitement par un gestionnaire (propagation vers un bloc englobant).
Java dispose d'un spécificateur pour ce signalement : le mot clef throws suivi de la liste des noms des classes d'exceptions qui sont propagées.
Signature générale d'une méthode propageant des exceptions vérifiées
<modificateurs> <type> < identificateur> ( <liste param formels> )
throws < liste d'exceptions> {
.....
}
Exemple :
protected static void meth ( int x, char c )
throws IOException, ArithmeticException {
.....
}
Nous allons choisir une classe parmi les très nombreuses d'exceptions vérifiées, notre choix se porte sur une classe d'exception très utilisée, la classe IOException qui traite de toutes les exceptions d'entrée-sortie. Le J2SE 1.4.1 donne la liste des classes dérivant de la classe IOException :
ChangedCharSetException, CharacterCodingException, CharConversionException, ClosedChannelException, EOFException, FileLockInterruptionException, FileNotFoundException, IIOException, InterruptedIOException, MalformedURLException, ObjectStreamException, ProtocolException, RemoteException, SocketException, SSLException, SyncFailedException, UnknownHostException, UnknownServiceException, UnsupportedEncodingException, UTFDataFormatException, ZipException.
Reprenons le programme écrit au paragraphe précédent concernant le déclenchement manuel d'une exception existante en l'appliquant à la classe d'exception existante IOException, cette classe étant incluse dans le package java.io et non dans le package java.lang importé implicitement, on doit ajouter une instruction d'importation du package java.io, puis exécutons le programme tel quel. Voici ce que nous obtenons :
Résultats de l'exécution :
---- java UseAction4
UseAction4.java:8: unreported exception java.io.IOException; must be caught or declared to be thrown
throw new IOException("Problème d'E/S !");
^
1 error
Que s'est-il passé ?
Le compilateur Java attendait de notre part l'une des deux seules attitudes suivantes :
Soit nous interceptons et traitons l'exception IOException à l'intérieur du corps de la méthode où elle a été lancée avec pour conséquence que le bloc englobant n'aura pas à traiter une telle exception puisque l'objet d'exception est automatiquement détruit dès qu'il a été traité. Le code ci-après implante cette première attitude :
Résultats de l'exécution :
---- java UseAction4
Début du programme.
...Avant incident
Interception exception : Problème d'E/S !
...Après incident
Fin du programme.
Soit nous interceptons et traitons l'exception IOException à l'intérieur du bloc englobant la méthode meth() qui a lancé l'exception, auquel cas il est obligatoire de signaler au compilateur que cette méthode meth() lance et propage une IOException non traitée. Le code ci-après implante la seconde attitude possible :
Résultats de l'exécution :
---- java UseAction4
Début du programme.
...Avant incident
Interception exception : Problème d'E/S !
Fin du programme.
Redéfinition d'une méthode propageant des exceptions vérifiées
Principe de base : la partie throws < liste d'exceptions> de la signature de la méthode qui redéfinit une méthode de la super-classe peut comporter moins de types d'exception. Elle ne peut pas propager plus de types ou des types différents de ceux de la méthode de la super-classe.
Ci-après un programme avec une super-classe Action4 et une méthode meth(), qui est redéfinie dans une classe fille nommée Action5 :
Voici la hiérarchie des classes utilisées :
+--java.lang.Exception
|
+--java.io.IOException
| |
| +--java.io.CharConversionException
|
+--java.lang.NoSuchFieldException
Notez que la méthode meth de la super-classe propage IOException et NoSuchFieldException bien qu'elle ne lance qu'une exception de type IOException; ceci permettra la redéfinition dans la classe fille.
Voici pour terminer, un code source de classe utilisant la classe Action5 précédemment définie et engendrant aléatoirement l'un des 3 types d"exception :
Analysez et comprenez bien le fonctionnement de ce petit programme.
Listing des 3 exécutions dans chacun des cas d'appel de la méthode meth :
Résultats de l'exécution
---- java UseAction5
Début du programme.
Appel meth(0)
...Avant incident-fille
...Avant incident-mère
Interception exception_0 : Problème conversion de caractère !-mère
Fin du programme.
Résultats de l'exécution
---- java UseAction5
Début du programme.
Appel meth(2)
...Avant incident-fille
Interception exception_2 : Problème de champ !-fille
Fin du programme.
Résultats de l'exécution
---- java UseAction5
Début du programme.
Appel meth(1)
...Avant incident-fille
Interception exception_1 : Problème d'E/S !-fille
Fin du programme.
1.8 Clause finally
Supposons que nous soyons en présence d'un code contenant une éventuelle levée d'exception, mais supposons que quoiqu'il se passe nous désirions qu'un certain type d'action ait toujours lieu (comme par exemple fermer un fichier qui a été ouvert auparavant). Il existe en Java une clause spécifique optionnelle dans la syntaxe des gestionnaires d'exception permettant ce type de réaction du programme, c'est la clause finally. Voici en pseudo Java une syntaxe de cette clause :
<Ouverture du fichier>
try {
< action sur fichier>
}
finally {
<fermeture du fichier>
}
.... suite
Fonctionnement dans le cas où rien n'a lieu
Si aucun incident ne se produit durant l'exécution du bloc < action sur fichier> :
l'exécution se poursuit à l'intérieur du bloc finally par l'action <fermeture du fichier> et la suite du code.
Fonctionnement si quelque chose se produit
Si un incident se produit durant l'exécution du bloc < action sur fichier> et qu'une exception est lancée:
malgré tout l'exécution se poursuivra à l'intérieur du bloc finally par l'action <fermeture du fichier>, puis arrêtera l'exécution du code dans le bloc.
La syntaxe Java autorise l'écriture d'une clause finally associée à plusieurs clauses catch :
try {
<code à protéger>
}
catch (exception1 e ) {
<traitement de l'exception1>}
catch (exception2 e ) {
<traitement de l'exception2>}
...
finally {
<action toujours effectuée>
}
Remarque :
Si le code du bloc à protéger dans try...finally contient une instruction de rupture de séquence comme break, return ou continue, le code de la clause finally{...} est malgré tout exécuté avant la rupture de séquence.
Nous avons vu au chapitre 1.2 § 5.2 sur la boucle while, la boucle for et l'instruction continue que l'équivalence suivante entre un for et un while valide dans le cas général, était mise en défaut si le corps d'instruction contenait un continue (instruction forçant l'arrêt d'un tours de boucle et relançant l'itération suivante) :
Equivalence incorrecte si Instr contient un continue :
for (Expr1 ; Expr2 ; Expr3 ) Instr Expr1 ;
while ( Expr2 )
{ Instr ;
Expr3
}
Equivalence correcte même si Instr contient un continue :
for (Expr1 ; Expr2 ; Expr3 ) Instr Expr1 ;
while ( Expr2 )
{
try {
Instr ;
} finally { Expr3
}
}
2. Un exemple complet de traitement
2.2 La démarche
Les éventuelles exceptions lancées par les blocs <OuvrirFichier>,<LireElement> et <FermerFichier> doivent pouvoir se propager aux blocs de niveaux englobant afin d'être interceptable à n'importe quel niveau.
Les classes d'exception
On propose de créer une classe générale d'exception EFichierError héritant de la classe des Exception de java, puis 3 classes d'exception héritant de cette classe EFichierError :
Modèle java pour ces 4 classes d'exception (à compléter) :
class EFichierError extends Exception{
public int typeErr; // stockage du numéro d'erreur pour utilisation ultérieure
......... }
class EOuvertureError extends EFichierError{
......... }
class ELectureError extends EFichierError{
......... }
class EFermetureError extends EFichierError{
......... }
Les blocs lançant éventuellement une exception
Chaque bloc le plus interne peut lancer (lever) une exception de classe différente et la propage au niveau supérieur :
Modèle java pour les appels de bloc et la propagation (à compléter) :
void ChercheElement( ) throws .....{
OuvrirFichier( );
LireElement( );
FermerFichier( );
}
void OuvrirFichier( ) throws .....{
System.out.println(" >> Action ouverture...");
GenererIncident(1);
System.out.println(" >> Fin ouverture.");
}
void LireElement( ) throws .....{
System.out.println(" >> Action lecture...");
GenererIncident(2);
System.out.println(" >> Fin lecture.");
}
void FermerFichier( ) throws .....{
System.out.println(" >> Action fermeture...");
GenererIncident(3);
System.out.println(" >> Fin fermeture.");
}
void GenererIncident(int TypeIncident) throws .....{
selon la valeur de l'entier TypeIncident
engendre aléatoirement ou non une exception de l'un des 3 types :
EOuvertureError, ELectureError ou EFermetureError
......
}
Les blocs interceptant les exceptions
Nous proposons par exemple d'intercepter les exceptions dans les deux blocs <ActionsSurFichier> et <AfficheFichier> :
Le bloc <AfficherFichier>
Ce bloc interceptera une exception de type EFichierError, puis la redéclenchera après traitement :
Modèle java pour ce bloc (à compléter) :
void AfficherFichier( ) throws .....{
try {
ChercheElement( );
}
catch ( EFichierError E ){
.............
}
}
Le bloc <ActionsSurFichier>
Ce bloc interceptera une exception de l'un des trois types EOuvertureError, ELectureError ou EFermetureError :
Modèle java pour ce bloc (à compléter) :
void ActionsSurFichier( ) throws ......{
System.out.println("Debut du travail sur le fichier.");
try{
System.out.println(".........");
AfficherFichier( );
}
catch .......
System.out.println("Fin du travail sur le fichier.");
}
Modèle java de la méthode main appelant le bloc <ActionsSurFichier> (à compléter) :
public static void main(String[] arg){
..... Obj .......
try {
Obj.ActionsSurFichier( );
}
catch ......
}
2.3 Un programme Java : une solution
Modèle java pour tous les blocs :
Le programme Java
Un code source java complet :
class EFichierError extends Exception{
public int typeErr;
public EFichierError( String s, int x){
super(s);
typeErr = x;
}
}
class EOuvertureError extends EFichierError{
public EOuvertureError( int x){
super("Impossible d'ouvrir le fichier !",x);
}
}
class ELectureError extends EFichierError{
public ELectureError( int x){
super("Impossible de lire le fichier !",x);
}
}
class EFermetureError extends EFichierError{
public EFermetureError( int x){
super("Impossible de fermer le fichier !",x);
}
}
class PrTransmettre{
void TraitementGen( String s){
System.out.println("traitement general de l''erreur: "+s);
}
void TraitementSpecif(String s){
System.out.println("traitement specifique de l''erreur: "+s);
}
void ChercheElement( ) throws EFichierError{
OuvrirFichier();
LireElement();
FermerFichier();
}
void OuvrirFichier( ) throws EFichierError{
System.out.println(" >> Action ouverture...");
GenererIncident (1);
System.out.println(" >> Fin ouverture.");
}
void LireElement( ) throws EFichierError{
System.out.println(" >> Action lecture...");
GenererIncident (2);
System.out.println(" >> Fin lecture.");
}
void FermerFichier( ) throws EFichierError{
System.out.println(" >> Action fermeture...");
GenererIncident (3);
System.out.println(" >> Fin fermeture.");
}
void GenererIncident ( int TypeIncident) throws EOuvertureError, ELectureError, EFermetureError {
// engendre aléatoirement ou non une exception de l'un des 3 types
int n;
switch (TypeIncident){
case 1: n = (int)(Math.random()*10)%4;
if (n==0) throw new EOuvertureError(TypeIncident);
break;
case 2: n=(int)(Math.random()*10)%3;
if (n==0) throw new ELectureError(TypeIncident);
break;
case 3:n=(int)(Math.random()*10)%2;
if (n==0) throw new EFermetureError(TypeIncident);
}
}
void ActionsSurFichier( ) throws EFichierError{
System.out.println("Debut du travail sur le fichier.");
try{
System.out.println(".........");
AfficherFichier();
}
catch(EOuvertureError E){
TraitementSpecif(E.getMessage( ));
}
catch(ELectureError E){
TraitementSpecif(E.getMessage( ));
}
catch(EFermetureError E){
TraitementSpecif(E.getMessage( ));
}
System.out.println("Fin du travail sur le fichier.");
}
void AfficherFichier( ) throws EFichierError {
try {
ChercheElement();
}
catch(EFichierError E){
TraitementGen(E.getMessage());
throw E;
}
}
public static void main(String[] arg){
PrTransmettre Obj = new PrTransmettre();
try {
Obj.ActionsSurFichier();
}
catch (EFichierError E){
System.out.println("Autre type d'Erreur générale Fichier !");
}
}
}
Les exécutions du programme
Exécution dans le cas où aucune exception n'est lancée :
Debut du travail sur le fichier.
.........
>> Action ouverture...
>> Fin ouverture.
>> Action lecture...
>> Fin lecture.
>> Action fermeture...
>> Fin fermeture.
Fin du travail sur le fichier.
Exécution dans le cas où une exception ELectureError est lancée :
Debut du travail sur le fichier.
.........
>> Action ouverture...
>> Fin ouverture.
>> Action lecture...
traitement general de l''erreur: Impossible de lire le fichier !
traitement specifique de l''erreur: Impossible de lire le fichier !
Fin du travail sur le fichier.
Exécution dans le cas où une exception EOuvertureError est lancée :
Debut du travail sur le fichier.
.........
>> Action ouverture...
traitement general de l''erreur: Impossible d'ouvrir le fichier !
traitement specifique de l''erreur: Impossible d'ouvrir le fichier !
Fin du travail sur le fichier.
Exécution dans le cas où une exception EFermetureError est lancée :
Debut du travail sur le fichier.
.........
>> Action ouverture...
>> Fin ouverture.
>> Action lecture...
>> Fin lecture.
>> Action fermeture...
traitement general de l''erreur: Impossible de fermer le fichier !
traitement specifique de l''erreur: Impossible de fermer le fichier !
Fin du travail sur le fichier.