 |
 |
10) Gestion des erreurs avec les exceptions |
|
 |
|
Texte original |
 |
Traducteur : N. CULWELL-KANAREK, Anthony PRAUD, A. FORTUN |
|
 |
///
|
Ce chapitre contient 3 pages
1
2
3
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
It can be a bit horrifying to think that
you must check for null on every reference that is passed into a method
(since you can’t know if the caller has passed you a valid reference).
Fortunately, you don’t—this is part of the standard run-time
checking that Java performs for you, and if any call is made to a null
reference, Java will automatically throw a
NullPointerException. So
the above bit of code is always superfluous.
|
 |
Cela peut paraître effrayant de penser que l'on devra vérifier le
null pour chaque référence passée à un e méthode(puisque on ne
sait pas si l'appelant à passé une référence valide). Heureusement
vous n'avez pas à le faire. C'est une des taches standard de run-time
checking que Java exécute pour vous, et si un appel est fait à une
référence contenant la valeur null, Java va automatiquement
générer une NullPointerException. Donc le code ci-dessus est
superflu.
|
 |
 |
 |
There’s a whole group of exception
types that are in this category. They’re always thrown automatically by
Java and you don’t need to include them in your exception specifications.
Conveniently enough, they’re all grouped together by putting them under a
single base class called RuntimeException, which is a perfect example of
inheritance: it establishes a family of types that have some characteristics and
behaviors in common. Also, you never need to write an exception specification
saying that a method might throw a RuntimeException, since that’s
just assumed. Because they indicate bugs, you virtually never catch a
RuntimeException—it’s
dealt with automatically. If you were forced to check for
RuntimeExceptions your code could get messy. Even though you don’t
typically catch RuntimeExceptions, in your own packages you might
choose to throw some of the RuntimeExceptions.
|
 |
Il y a tout un groupe d'exceptions qui sont dans cette catégorie.
Elles sont générées automatiquement par Java et vous n'avez pas besoin
de les inclure dans vos spécifications d'exception. Elles sont
regroupées dans une seule classe de base nommée RuntimeExceptions, qui
est un parfait exemple de l'héritage : cela établit une famille de
types qui ont des caractéristiques et des comportements communs. Aussi
vous n'avez jamais besoin de spécifier qu'une méthode peut générer une
RuntimeException puisque c'est géré. Puisque cela indique la présence
de bogues vous n'interceptez jamais de RuntimeException c'est
géré automatiquement. Si vous étiez obligés de tester les
RuntimeException votre code pourrait devenir illisible. Bien que vous
n'interceptiez pas de RuntimeException dans vos propre paquettages
vous pourriez décider de générer des RuntimeException.
|
 |
 |
 |
What happens when you don’t catch
such exceptions? Since the compiler doesn’t enforce exception
specifications for these, it’s quite plausible that a
RuntimeException could percolate all the way out to your main( )
method without being caught. To see what happens in this case, try the
following example:
|
 |
Que se passe t'il quand vous n'interceptez pas de telles exceptions ?
Comme le compilateur ne demande pas de spécifications d'exceptions
pour celles ci il est probable qu'une RuntimeException puisse
remonter jusqu'à votre méthode main() sans être interceptée.
Pour voir ce qui se passe dans ce cas là essayez l'exemple suivant :
|
 |
 |
 |
//: c10:NeverCaught.java // Ignoring RuntimeExceptions.
public class NeverCaught { static void f() { throw new RuntimeException("From f()"); } static void g() { f(); } public static void main(String[] args) { g(); } } ///:~
|
 |
//: c10:NeverCaught.java // Ignoring RuntimeExceptions. public "#0000FF">class NeverCaught { static "#0000FF">void f() { throw "#0000FF">new RuntimeException("#004488">"From f()"); } static "#0000FF">void g() { f(); } public "#0000FF">static "#0000FF">void main(String[] args) { g(); } } ///:~
|
 |
 |
 |
You can already see that a
RuntimeException (or anything inherited from it) is a special case, since
the compiler doesn’t require an exception specification for these
types.
|
 |
vous pouvez déjà voir qu'une RuntimeException (ou tout ce qui
en hérite) est un cas particulier, puisque le compilateur ne demande
pas de spécifications pour ce type.
|
 |
 |
 |
The output is:
|
 |
La trace est :
|
 |
 |
 |
Exception in thread "main" java.lang.RuntimeException: From f() at NeverCaught.f(NeverCaught.java:9) at NeverCaught.g(NeverCaught.java:12) at NeverCaught.main(NeverCaught.java:15)
|
 |
Exception in thread "main" java.lang.RuntimeException: From f() at NeverCaught.f(NeverCaught.java:9) at NeverCaught.g(NeverCaught.java:12) at NeverCaught.main(NeverCaught.java:15)
|
 |
 |
 |
So the answer is: If a
RuntimeException gets all the way out to main( ) without
being caught, printStackTrace( ) is called for that exception as the
program exits.
|
 |
La réponse est donc : Si une RuntimeException arrive jusqu'à la
méthode main() sans être interceptée, PrintStackTrace()
est appelée pour cette exception à la sortie du programme.
|
 |
 |
 |
Keep in mind that you can only ignore
RuntimeExceptions in your coding, since all other handling is carefully
enforced by the compiler. The reasoning is that a RuntimeException
represents a programming error:
|
 |
Gardez à l'esprit que vous pouvez ignorer seulement les
RuntimeException dans vos développements puisque la gestion de
toutes les autres exceptions est renforcée par le compilateur. Le
raisonnement est qu'une RuntimeRxception représente une erreur
de programmation.
|
 |
 |
 |
- An error you cannot catch
(receiving a null reference handed to your method by a client programmer,
for example) .
- An
error that you, as a programmer, should have checked for in your code (such as
ArrayIndexOutOfBoundsException where you should have paid attention to
the size of the array).
|
 |
-
Une erreur que vous ne pouvez pas intercepter( recevoir une
référence transmise par un programme client avec la valeur null par
exemple).
-
Une erreur que vous auriez du prévoir dans votre code (comme
ArrayIndexOutOfBoundsException ou vous auriez du faire
attention à la taille du tableau).
|
 |
 |
 |
You
can see what a tremendous benefit it is to have exceptions in this case, since
they help in the debugging process.
|
 |
Vous pouvez voir quel bénéfice apportent les exceptions dans ce cas
puisque cela aide le processus de déboguage.
|
 |
 |
 |
It’s interesting to notice that you
cannot classify Java exception handling as a single-purpose tool. Yes, it is
designed to handle those pesky run-time errors that will occur because of forces
outside your code’s control, but it’s also essential for certain
types of programming bugs that the compiler cannot
detect.
|
 |
Il est intéressant de noter que vous ne pouvez pas ranger le gestion
des exceptions Java dans un outil à utilisation unique. Oui c'est fait
pour gérer c'est affreuses run-time error qui vont apparaître à causes
de forces hors de contrôle de votre code mais c'est aussi essentiel
pour certains types de bogues de développement que le compilateur ne
peut détecter.
|
 |
 |
 |
Performing cleanup with finally
|
 |
Faire le ménage avec finally
|
 |
 |
 |
There’s often some piece of code
that you want to execute whether or not an exception is thrown within a
try block. This usually pertains to some operation other than memory
recovery (since that’s taken care of by the garbage collector). To achieve
this effect, you use a
finally
clause[53] at the
end of all the exception handlers. The full picture of an exception handling
section is thus:
|
 |
Il y a souvent un morceau de code que vous voulez exécuter qu'une
exception ou pas soit générée au coeur d'un bloc try. C'est
généralement destiné à des opérations autre que le rafraîchissement de
la mémoire (puisque le garbage collector s'en occupe). Pour faire ceci
il faut utiliser finally à la fin de chaque gestionnaire
d'exceptions. L'image complète d'un paragraphe gestion d'exception est
:
|
 |
 |
 |
try { // The guarded region: Dangerous activities // that might throw A, B, or C } catch(A a1) { // Handler for situation A } catch(B b1) { // Handler for situation B } catch(C c1) { // Handler for situation C } finally { // Activities that happen every time }
|
 |
try { // The guarded region: Dangerous activities // that might throw A, B, or C } catch(A a1) { // Handler for situation A } catch(B b1) { // Handler for situation B } catch(C c1) { // Handler for situation C } finally { // Activities that happen every time }
|
 |
 |
 |
To demonstrate that the finally
clause always runs, try this program:
|
 |
Pour démontrer que la clause finally est toujours exécutée
lancer ce programme :
|
 |
 |
 |
//: c10:FinallyWorks.java // The finally clause is always executed.
class ThreeException extends Exception {}
public class FinallyWorks { static int count = 0; public static void main(String[] args) { while(true) { try { // Post-increment is zero first time: if(count++ == 0) throw new ThreeException(); System.out.println("No exception"); } catch(ThreeException e) { System.err.println("ThreeException"); } finally { System.err.println("In finally clause"); if(count == 2) break; // out of "while" } } } } ///:~
|
 |
//: c10:FinallyWorks.java // The finally clause is always executed. class ThreeException "#0000FF">extends Exception {} public "#0000FF">class FinallyWorks { static "#0000FF">int count = 0; public "#0000FF">static "#0000FF">void main(String[] args) { while(true) { try { // Post-increment is zero first time: if(count++ == 0) throw "#0000FF">new ThreeException(); System.out.println("No exception"); } catch(ThreeException e) { System.err.println("ThreeException"); } finally { System.err.println("#004488">"In finally clause"); if(count == 2) "#0000FF">break; // out of "while" } } } } ///:~
|
 |
 |
 |
This program also gives a hint for how
you can deal with the fact that exceptions in Java (like exceptions in C++) do
not allow you to resume back to where the exception was thrown, as discussed
earlier. If you place your try block in a loop, you can establish a
condition that must be met before you continue the program. You can also add a
static counter or some other device to allow the loop to try several
different approaches before giving up. This way you can build a greater level of
robustness into your programs.
|
 |
Ce programme vous donne aussi une aide pour comprendre comment gérer
le fait que Java(comme les exceptions en C++) ne vous permet de
reprendre l'exécution de l'endroit où l'exception a été générée, comme
vu plus tôt. Si vous placez votre bloc try dans une boucle,
vous pouvez établir un condition qui doit être validée avant de
continuer le programme. Vous pouvez aussi ajouter un compteur de type
static ou d'autres périphériques afin de permettre à la boucle
différente approches avant d'abandonner. De cette façon vous pouvez
obtenir un plus grand niveau de robustesse dans vos programmes.
|
 |
 |
 |
The output is:
|
 |
La sortie est :
|
 |
 |
 |
ThreeException In finally clause No exception In finally clause
|
 |
ThreeException In finally clause No exception In finally clause
|
 |
 |
 |
Whether an exception is thrown or not,
the finally clause is always
executed.
|
 |
Qu'une exception soit générée ou pas la clause finally est
exécutée.
|
 |
 |
 |
What’s finally for?
|
 |
À Quoi sert le finally ?
|
 |
 |
 |
In a language without garbage collection
and without automatic destructor
calls[54],
finally is important because it allows the programmer to guarantee the
release of memory regardless of what happens in the
try block. But Java has
garbage collection, so releasing memory is virtually never a problem. Also, it
has no destructors to call. So when do you need to use
finally in Java?
|
 |
Dans un langage sans ramasse-miettes [Garbage Collector] et
sans appel automatique aux destructeurs, finally est important
parce qu'il permet au programmeur de garantir la libération de la
mémoire indépendamment de ce qui se passe dans le bloc try.
Mais Java a un ramasse-miettes. Donc il n'a aucun destructeurs à
appeler. Donc quand avez vous besoin de la clause finally en
Java ?
|
 |
 |
 |
finally is necessary when you need
to set something other than memory back to its original state. This is
some kind of cleanup like an open file or network connection, something
you’ve drawn on the screen, or even a switch in the outside world, as
modeled in the following example:
|
 |
Finally est nécessaire quand vous avez besoin de remettre à
l'état original autre chose que le mémoire. C'est une sorte de
nettoyage comme un fichier ouvert une connexion réseau quelque chose
affiché à l'écran ou un switch dans le monde extérieur comme modélisé
dans l'exemple suivant :
|
 |
 |
 |
//: c10:OnOffSwitch.java // Why use finally?
class Switch { boolean state = false; boolean read() { return state; } void on() { state = true; } void off() { state = false; } } class OnOffException1 extends Exception {} class OnOffException2 extends Exception {}
public class OnOffSwitch { static Switch sw = new Switch(); static void f() throws OnOffException1, OnOffException2 {} public static void main(String[] args) { try { sw.on(); // Code that can throw exceptions... f(); sw.off(); } catch(OnOffException1 e) { System.err.println("OnOffException1"); sw.off(); } catch(OnOffException2 e) { System.err.println("OnOffException2"); sw.off(); } } } ///:~
|
 |
//: c10:OnOffSwitch.java // Why use finally? class Switch { boolean state = "#0000FF">false; boolean read() { "#0000FF">return state; } void on() { state = "#0000FF">true; } void off() { state = "#0000FF">false; } } class OnOffException1 "#0000FF">extends Exception {} class OnOffException2 "#0000FF">extends Exception {} public "#0000FF">class OnOffSwitch { static Switch sw = "#0000FF">new Switch(); static "#0000FF">void f() throws OnOffException1, OnOffException2 {} public "#0000FF">static "#0000FF">void main(String[] args) { try { sw.on(); // Code that can throw exceptions... f(); sw.off(); } catch(OnOffException1 e) { System.err.println("OnOffException1"); sw.off(); } catch(OnOffException2 e) { System.err.println("OnOffException2"); sw.off(); } } } ///:~
|
 |
 |
 |
The goal here is to make sure that the
switch is off when main( ) is completed, so sw.off( ) is
placed at the end of the try block and at the end of each exception handler. But
it’s possible that an exception could be thrown that isn’t caught
here, so sw.off( ) would be missed. However, with finally you
can place the cleanup code from a try block in just one place:
|
 |
L'objectif ici est d'être sûr que le switch est Off quand la méthode
main() se termine donc sw.off() est placée à la fin de
main() et à la fin de chaque gestionnaire d'exception mais il
se peut qu'une exception soit générée et qu'elle ne soit pas
interceptée ici. Vous pouvez placer le code de nettoyage dans un seul
endroit le bloc finally :
|
 |
 |
 |
//: c10:WithFinally.java // Finally Guarantees cleanup.
public class WithFinally { static Switch sw = new Switch(); public static void main(String[] args) { try { sw.on(); // Code that can throw exceptions... OnOffSwitch.f(); } catch(OnOffException1 e) { System.err.println("OnOffException1"); } catch(OnOffException2 e) { System.err.println("OnOffException2"); } finally { sw.off(); } } } ///:~
|
 |
//: c10:WithFinally.java // Finally Guarantees cleanup. public "#0000FF">class WithFinally { static Switch sw = "#0000FF">new Switch(); public "#0000FF">static "#0000FF">void main(String[] args) { try { sw.on(); // Code that can throw exceptions... OnOffSwitch.f(); } catch(OnOffException1 e) { System.err.println("OnOffException1"); } catch(OnOffException2 e) { System.err.println("OnOffException2"); } finally { sw.off(); } } } ///:~
|
 |
 |
 |
Here the sw.off( ) has been
moved to just one place, where it’s guaranteed to run no matter what
happens.
|
 |
Ici le sw.off() à été placée à un endroit unique ou on est sûr
qu'il sera exécutée quoi qu'il arrive.
|
 |
 |
 |
Even in cases in which the exception is
not caught in the current set of catch clauses, finally will be
executed before the exception handling mechanism continues its search for a
handler at the next higher level:
|
 |
Même dans le cas où l'exception n'est pas interceptée dans l'ensemble
des clauses catch, finally sera exécuté avant que le
mécanisme de gestion d'exception recherche un gestionnaire de plus
haut niveau.
|
 |
 |
 |
//: c10:AlwaysFinally.java // Finally is always executed.
class FourException extends Exception {}
public class AlwaysFinally { public static void main(String[] args) { System.out.println( "Entering first try block"); try { System.out.println( "Entering second try block"); try { throw new FourException(); } finally { System.out.println( "finally in 2nd try block"); } } catch(FourException e) { System.err.println( "Caught FourException in 1st try block"); } finally { System.err.println( "finally in 1st try block"); } } } ///:~
|
 |
//: c10:AlwaysFinally.java // Finally is always executed. class FourException "#0000FF">extends Exception {} public "#0000FF">class AlwaysFinally { public "#0000FF">static "#0000FF">void main(String[] args) { System.out.println( "Entering first try block"); try { System.out.println( "Entering second try block"); try { throw "#0000FF">new FourException(); } finally { System.out.println( "finally in 2nd try block"); } } catch(FourException e) { System.err.println( "#004488">"Caught FourException in 1st try block"); } finally { System.err.println( "finally in 1st try block"); } } } ///:~
|
 |
 |
 |
The output for this program shows you
what happens:
|
 |
La sortie de ce programme vous montre ce qui arrive :
|
 |
 |
 |
Entering first try block Entering second try block finally in 2nd try block Caught FourException in 1st try block finally in 1st try block
|
 |
Entering first try block Entering second try block finally in 2nd "#0000FF">try block Caught FourException in 1st try block finally in 1st "#0000FF">try block
|
 |
 |
 |
The finally statement will also be
executed in situations in which break and continue statements are
involved. Note that, along with the labeled break and labeled
continue, finally eliminates the need for a goto statement
in Java.
|
 |
Le mot clé finally sera aussi exécuté dans des situations ou
les mots clés break et continue sont impliqués. Notez
qu'avec break continue et finally il n'y a pas besoin de goto
en Java.
|
 |
 |
 |
Pitfall: the lost exception
|
 |
Le défaut : l'exception perdue
|
 |
 |
 |
In general, Java’s exception
implementation is quite outstanding, but unfortunately there’s a flaw.
Although exceptions are an indication of a crisis in your program and should
never be ignored, it’s possible for an exception to simply be
lost. This happens with a particular configuration using
a finally clause:
|
 |
En général l'implémentation de Java est de grande qualité mais
malheureusement il y a un petit défaut. Bien qu'une exception soit une
indication de crise dans votre programme et ne doit jamais être ignorée
il est possible qu'une exception soit perdue. Cela est possible dans une
certaine configuration de la clause finally :
|
 |
 |
 |
//: c10:LostMessage.java // How an exception can be lost.
class VeryImportantException extends Exception { public String toString() { return "A very important exception!"; } }
class HoHumException extends Exception { public String toString() { return "A trivial exception"; } }
public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main(String[] args) throws Exception { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); } } } ///:~
|
 |
//: c10:LostMessage.java // How an exception can be lost. class VeryImportantException "#0000FF">extends Exception { public String toString() { return "#004488">"A very important exception!"; } } class HoHumException "#0000FF">extends Exception { public String toString() { return "#004488">"A trivial exception"; } } public "#0000FF">class LostMessage { void f() "#0000FF">throws VeryImportantException { throw "#0000FF">new VeryImportantException(); } void dispose() "#0000FF">throws HoHumException { throw "#0000FF">new HoHumException(); } public "#0000FF">static "#0000FF">void main(String[] args) throws Exception { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); } } } ///:~
|
 |
 |
 |
The output is:
|
 |
Le résultat est :
|
 |
 |
 |
Exception in thread "main" A trivial exception at LostMessage.dispose(LostMessage.java:21) at LostMessage.main(LostMessage.java:29)
|
 |
Exception in thread "#004488">"main" A trivial exception at LostMessage.dispose(LostMessage.java:21) at LostMessage.main(LostMessage.java:29)
|
 |
 |
 |
You can see that there’s no
evidence of the VeryImportantException, which is simply replaced by the
HoHumException in the finally clause. This is a rather serious
pitfall, since it means that an exception can be completely lost, and in a far
more subtle and difficult-to-detect fashion than the example above. In contrast,
C++ treats the situation in which a second exception is thrown before the first
one is handled as a dire programming error. Perhaps a future version of Java
will repair this problem (on the other hand, you will typically wrap any method
that throws an exception, such as dispose( ), inside a
try-catch
clause).
|
 |
Vous pouvez voir qu'il ne reste aucune trace de
VeryImportantException qui est tout simplement remplacée par
HoHumException dans la clause finally. Ceci est un
faille sérieuse puisqu'il signifie qu'une exception peut être
complètement perdue et bien plus difficile et subtile à détecter que
les autres. C++ lui au contraire traite la situation en considérant
qu'une seconde exception générée avant la première est une erreur de
programmation. Peut être qu' une future version de Java corrigera ce
défaut (autrement vous pouvez exclure du bloc try-catch toute
méthode susceptible de générer une exception telle que
dispose()).
|
 |
 |
 |
Exception restrictions
|
 |
Restriction d'Exceptions
|
 |
 |
 |
When you override a method, you can throw
only the exceptions that have been specified in the base-class version of the
method. This is a useful restriction, since it means that code that works with
the base class will automatically work with any object derived from the base
class (a fundamental OOP concept, of course), including
exceptions.
|
 |
Quand vous surchargez une méthode vous ne pouvez générer que les
exceptions qui ont été spécifiées dans la classe de base de la version
de la méthode. c'est une restriction très utile puisqu'elle garantit
que le code qui fonctionne avec la version de classe de base de la
méthode fonctionnera avec tout les objets dérivant de cette classe (
un concept fondamental de la POO),incluant les exceptions.
|
 |
 |
 |
This example demonstrates the kinds of
restrictions imposed (at compile-time) for exceptions:
|
 |
Cet exemple démontre les restrictions imposée (à la compilation) pour
les exceptions :
|
 |
 |
 |
//: c10:StormyInning.java // Overridden methods may throw only the // exceptions specified in their base-class // versions, or exceptions derived from the // base-class exceptions.
class BaseballException extends Exception {} class Foul extends BaseballException {} class Strike extends BaseballException {}
abstract class Inning { Inning() throws BaseballException {} void event () throws BaseballException { // Doesn't actually have to throw anything } abstract void atBat() throws Strike, Foul; void walk() {} // Throws nothing }
class StormException extends Exception {} class RainedOut extends StormException {} class PopFoul extends Foul {}
interface Storm { void event() throws RainedOut; void rainHard() throws RainedOut; }
public class StormyInning extends Inning implements Storm { // OK to add new exceptions for // constructors, but you must deal // with the base constructor exceptions: StormyInning() throws RainedOut, BaseballException {} StormyInning(String s) throws Foul, BaseballException {} // Regular methods must conform to base class: //! void walk() throws PopFoul {} //Compile error // Interface CANNOT add exceptions to existing // methods from the base class: //! public void event() throws RainedOut {} // If the method doesn't already exist in the // base class, the exception is OK: public void rainHard() throws RainedOut {} // You can choose to not throw any exceptions, // even if base version does: public void event() {} // Overridden methods can throw // inherited exceptions: void atBat() throws PopFoul {} public static void main(String[] args) { try { StormyInning si = new StormyInning(); si.atBat(); } catch(PopFoul e) { System.err.println("Pop foul"); } catch(RainedOut e) { System.err.println("Rained out"); } catch(BaseballException e) { System.err.println("Generic error"); } // Strike not thrown in derived version. try { // What happens if you upcast? Inning i = new StormyInning(); i.atBat(); // You must catch the exceptions from the // base-class version of the method: } catch(Strike e) { System.err.println("Strike"); } catch(Foul e) { System.err.println("Foul"); } catch(RainedOut e) { System.err.println("Rained out"); } catch(BaseballException e) { System.err.println( "Generic baseball exception"); } } } ///:~
|
 |
//: c10:StormyInning.java // Overridden methods may throw only the // exceptions specified in their base-class // versions, or exceptions derived from the // base-class exceptions. class BaseballException "#0000FF">extends Exception {} class Foul "#0000FF">extends BaseballException {} class Strike "#0000FF">extends BaseballException {} abstract "#0000FF">class Inning { Inning() throws BaseballException {} void event () "#0000FF">throws BaseballException { // Doesn't actually have to throw anything } abstract "#0000FF">void atBat() "#0000FF">throws Strike, Foul; void walk() {} "#009900">// Throws nothing } class StormException "#0000FF">extends Exception {} class RainedOut "#0000FF">extends StormException {} class PopFoul "#0000FF">extends Foul {} interface Storm { void event() "#0000FF">throws RainedOut; void rainHard() "#0000FF">throws RainedOut; } public "#0000FF">class StormyInning "#0000FF">extends Inning implements Storm { // OK to add new exceptions for // constructors, but you must deal // with the base constructor exceptions: StormyInning() throws RainedOut, BaseballException {} StormyInning(String s) throws Foul, BaseballException {} "#009900">// Regular methods must conform to base class: "#009900">//! void walk() throws PopFoul {} //Compile error "#009900">// Interface CANNOT add exceptions to existing // methods from the base class: //! public void event() throws RainedOut {} // If the method doesn't already exist in the // base class, the exception is OK: public "#0000FF">void rainHard() "#0000FF">throws RainedOut {} "#009900">// You can choose to not throw any exceptions, // even if base version does: public "#0000FF">void event() {} // Overridden methods can throw // inherited exceptions: void atBat() "#0000FF">throws PopFoul {} public "#0000FF">static "#0000FF">void main(String[] args) { try { StormyInning si = new StormyInning(); si.atBat(); } catch(PopFoul e) { System.err.println("Pop foul"); } catch(RainedOut e) { System.err.println("Rained out"); } catch(BaseballException e) { System.err.println("Generic error"); } // Strike not thrown in derived version. try { // What happens if you upcast? Inning i = new StormyInning(); i.atBat(); // You must catch the exceptions from the // base-class version of the method: } catch(Strike e) { System.err.println("Strike"); } catch(Foul e) { System.err.println("Foul"); } catch(RainedOut e) { System.err.println("Rained out"); } catch(BaseballException e) { System.err.println( "Generic baseball exception"); } } } ///:~
|
 |
 |
 |
In Inning, you can see that both
the constructor and the event( ) method say they will throw an
exception, but they never do. This is legal because it allows you to force the
user to catch any exceptions that might be added in overridden versions of
event( ). The same idea holds for abstract methods, as seen
in atBat( ).
|
 |
Dans Inning() vous pouvez voir qu'à la fois le constructeur et
la méthode event() déclarent qu'elles peuvent lancer une
exception alors qu'elles ne le feront jamais. C'est tout à fait
légitime puisqu'elles vous laissent la possibilité de forcer
l'utilisateur à attraper des exceptions dans des versions surchargées
de event(). Le même principe s'applique pour les méthodes
abstraites [abstract].
|
 |
 |
 |
The interface Storm is interesting
because it contains one method (event( )) that is defined in
Inning, and one method that isn’t. Both methods throw a new type of
exception, RainedOut. When StormyInning extends Inning and
implements Storm, you’ll see that the event( ) method
in Storm cannot change the exception interface of
event( ) in Inning. Again, this makes sense because otherwise
you’d never know if you were catching the correct thing when working with
the base class. Of course, if a method described in an interface is not
in the base class, such as rainHard( ), then there’s no
problem if it throws exceptions.
|
 |
L'interface Storm est intéressante parce qu'elle contient une
méthode qui est définie dans Inning et une autre qui ne l'est
pas. Toutes génèrent un nouveau type d'exception, RainedOut.
Quand vous regardez StormyInning extends Inning implements
Storm. Vous voyez que la méthode event() de Storm
ne peut pas changer l'interface de la méthode event() de
Inning. Une fois encore cela est sensé car lorsque que vous
travaillez avec la classe de base vous ne sauriez pas si vous attrapez
la bonne chose. Bien sûr si un méthode décrite dans l'interface n'est
pas dans la classe de base telle que Rainhard(), ainsi il n'y a
aucun problème lorsqu'elle génèrent des exceptions.
|
 |
 |
 |
The restriction on exceptions does not
apply to constructors. You can
see in StormyInning that a constructor can throw anything it wants,
regardless of what the base-class constructor throws. However, since a
base-class constructor must always be called one way or another (here, the
default constructor is called automatically), the derived-class constructor must
declare any base-class constructor exceptions in its exception specification.
Note that a derived-class constructor cannot catch exceptions thrown by its
base-class constructor.
|
 |
La restriction sur les exceptions ne s'appliquent pas aux
constructeurs. Vous pouvez voir dans Stormyinning un
constructeur qui peut générer toutes les exceptions qu'il veut sans
être contraint par le constructeur de base. Malgré tout puis un
constructeur de classe de base doit bien être appelé à un moment ou à
un autre (ici le constructeur par défaut est appelé automatiquement)
le constructeur de la classe dérivée doit déclarer toute les
exceptions du constructeur de base dans ses spécifications
d'exception. un constructeur de classe dérivée ne peut pas intercepter
les exceptions de sa classe de base.
|
 |
 |
 |
The reason
StormyInning.walk( ) will not compile is that it throws an
exception, while Inning.walk( ) does not. If this was allowed, then
you could write code that called Inning.walk( ) and that
didn’t have to handle any exceptions, but then when you substituted an
object of a class derived from Inning, exceptions would be thrown so your
code would break. By forcing the derived-class methods to conform to the
exception specifications of the base-class methods, substitutability of objects
is maintained.
|
 |
La raison pour laquelle StormyInning.Walk() ne compilera pas
est qu'elle génère une exception alors que Inning.walk() n'en génère
pas. Si cela était permis vous pourriez écrire le code qui appelle
Inning.walk() et qui n'aurait pas à gérer une seule exception
mais lorsque vous substitueriez à Inning un objet d'un de ses
classes dérivées des exceptions seraient levées et votre code serait
interrompu. En forçant les méthodes de classes dérivées à se conformer
aux spécifications d'exceptions des classes de base la substituabilité
des objets est maintenue.
|
 |
 |
 |
The overridden event( )
method shows that a derived-class version of a method may choose not to throw
any exceptions, even if the base-class version does. Again, this is fine since
it doesn’t break any code that is written—assuming the base-class
version throws exceptions. Similar logic applies to atBat( ), which
throws PopFoul, an exception that is derived from Foul thrown by
the base-class version of atBat( ). This way, if someone writes code
that works with Inning and calls atBat( ), they must catch
the Foul exception. Since PopFoul is derived from Foul, the
exception handler will also catch PopFoul.
|
 |
La méthode surchargée event() montre qu'un méthode héritée
d'une classe peut choisir de ne pas générer d'exceptions même si la
méthode de la classe de base le fait. Ceci est de nouveau bon puisque
cela ne rend caduque aucun code qui a été écrit présumant que la
classe de base génère des exceptions. La même logique s'applique à
atBat() qui génère une exception PopFoul, une exception
qui est dérivée de l'exception Foul par la version de base de
atBat(). De cette façon si quelqu'un écrit du code qui
fonctionne avec Inning() et qui appelle atBat() doit
intercepter les exception Foul. Puisque PopFoul hérite
de Foul le gestionnaire d'exception interceptera
PopFoul.
|
 |
 |
 |
The last point of interest is in
main( ). Here you can see that if you’re dealing with exactly
a StormyInning object, the compiler forces you to catch only the
exceptions that are specific to that class, but if you upcast to the base type
then the compiler (correctly) forces you to catch the exceptions for the base
type. All these constraints produce much more robust exception-handling
code[55].
|
 |
Le dernier point intéressant est main() Ici vous pouvez voir
que si vous avez à faire à un objet StormyInning le compilateur vous
oblige à intercepter que les exceptions qui sont spécifiques à la
classe mais si vous caster l'objet le compilateur vous oblige (avec
raison) à gérer toutes les exceptions du type de base. Toute ces
contraintes produisent du code de gestion des exceptions beaucoup plus
robustes.
|
 |
 |
 |
It’s useful to realize that
although exception specifications are enforced by the compiler during
inheritance, the exception specifications are not part of the type of a method,
which is comprised of only the method name and argument types. Therefore, you
cannot overload methods based on exception specifications. In addition, just
because an exception specification exists in a base-class version of a method
doesn’t mean that it must exist in the derived-class version of the
method. This is quite different from inheritance rules, where a method in the
base class must also exist in the derived class. Put another way, the
“exception specification interface” for a particular method may
narrow during inheritance and overriding, but it may not widen—this is
precisely the opposite of the rule for the class interface during
inheritance.
|
 |
Il est utile de réaliser que bien la spécifications des exceptions
soit renforcée au moment de l'héritage par le compilateur, la
spécification des exceptions n'est pas une partie d'un type d'une
méthode, qui est composée seulement d'un nom de méthode et d'un type
d'arguments. vous ne pouvez pas surcharger une méthode basée sur une
spécification d'exception. De plus il n'est pas obligatoire qu'une
exception spécifiée dans méthode d'une classe de base soit présente
dans la méthode d'une classe en héritant. C'est assez différent des
règles de l'héritage, où un méthode qui existe dans une classe de base
doit aussi exister dans sa fille. Autrement dit l'interface de
spécification des exceptions d'une classe ne peut que se restreindre
alors que c'est le contraire pour l'interface de la classe durant
l'héritage.
|
 |
 |
 |
Constructors
|
 |
Les constructeurs
|
 |
 |
 |
When writing code with exceptions,
it’s particularly important that you always ask, “If an exception
occurs, will this be properly cleaned up?” Most of the time you’re
fairly safe, but in constructors there’s a problem. The constructor puts
the object into a safe starting state, but it might perform some
operation—such as opening a file—that doesn’t get cleaned up
until the user is finished with the object and calls a special cleanup method.
If you throw an exception from inside a constructor, these cleanup behaviors
might not occur properly. This means that you must be especially diligent while
you write your constructor.
|
 |
Quand on écrit du codes avec des spécifications d'exceptions il y a
une question particulière que l'on doit se poser « si une exception se
produit est ce que la situation sera rétablie à l'état initial ? »
dans la plupart des cas vous êtes sauvés mais dans celui des
constructeurs il y a un problème. Le constructeur place l'objet dans
un état de démarrage sain mais peut accomplir certaines opérations,
telles que ouvrir un fichier- qui ne seront pas terminées tant que
l'utilisateur n'aura pas terminé de travailler avec l'objet et appelé
une certaine méthode de restauration. Si vous générez une exception
dans le constructeur ces restaurations peuvent ne pas s'effectuer de
façon appropriée. Cela signifie que vous devez être extrêmement
prudent quand vous écrivez votre propre constructeur.
|
 |
 |
 |
Since you’ve just learned about
finally, you might think
that it is the correct solution. But it’s not quite that simple, because
finally performs the cleanup code every time, even in the
situations in which you don’t want the cleanup code executed until the
cleanup method runs. Thus, if you do perform cleanup in finally, you must
set some kind of flag when the constructor finishes normally so that you
don’t do anything in the finally block if the flag is set. Because
this isn’t particularly elegant (you are coupling your code from one place
to another), it’s best if you try to avoid performing this kind of cleanup
in finally unless you are forced to.
|
 |
Comme vous avez venez d'apprendre le mot clé finally vous
pouvez penser que c'est la solution mais ce n'est pas si simple, parce
que Finally fait le nettoyage du code à chaque exécution même dans les
cas ou vous ne le souhaiteriez pas. Aussi si vous faites la
restauration dans le finally vous devez placer un indicateur
qui vous indiquera de ne rien faire dans le finally si le constructeur
c'est bien déroulé mais cela n'est pas particulièrement élégant (vous
liez votre code d'un endroit à un autre) c'est mieux de ne pas faire
ce genre d'opérations dans le finally à moins d'y être forcé.
|
 |
 |
 |
In the following example, a class called
InputFile is created that opens a file and allows you to read it one line
(converted into a String) at a time. It uses the classes
FileReader and
BufferedReader from the
Java standard I/O library that will be discussed in Chapter 11, but which are
simple enough that you probably won’t have any trouble understanding their
basic use:
|
 |
Dans l'exemple suivant une classe appelée InputFile est crée
elle vous permet d'ouvrir un fichier et d'en lire une ligne (convertie
en String) à la fois . Elle utilise les classes
FileReader et InputBuffer de la librairie I/O standard
de Java dont nous discuterons au chapitre 11 mais elles sont assez
simple donc vous n'aurez probablement pas de problème à comprendre
leur comportement de base :
|
 |
 |
 |
//: c10:Cleanup.java // Paying attention to exceptions // in constructors. import java.io.*;
class InputFile { private BufferedReader in; InputFile(String fname) throws Exception { try { in = new BufferedReader( new FileReader(fname)); // Other code that might throw exceptions } catch(FileNotFoundException e) { System.err.println( "Could not open " + fname); // Wasn't open, so don't close it throw e; } catch(Exception e) { // All other exceptions must close it try { in.close(); } catch(IOException e2) { System.err.println( "in.close() unsuccessful"); } throw e; // Rethrow } finally { // Don't close it here!!! } } String getLine() { String s; try { s = in.readLine(); } catch(IOException e) { System.err.println( "readLine() unsuccessful"); s = "failed"; } return s; } void cleanup() { try { in.close(); } catch(IOException e2) { System.err.println( "in.close() unsuccessful"); } } }
public class Cleanup { public static void main(String[] args) { try { InputFile in = new InputFile("Cleanup.java"); String s; int i = 1; while((s = in.getLine()) != null) System.out.println(""+ i++ + ": " + s); in.cleanup(); } catch(Exception e) { System.err.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(System.err); } } } ///:~
|
 |
//: c10:Cleanup.java // Paying attention to exceptions // in constructors. import java.io.*; class InputFile { private BufferedReader in; InputFile(String fname) throws Exception { try { in = new BufferedReader( new FileReader(fname)); // Other code that might throw exceptions } catch(FileNotFoundException e) { System.err.println( "Could not open " + fname); // Wasn't open, so don't close it throw e; } catch(Exception e) { // All other exceptions must close it try { in.close(); } catch(IOException e2) { System.err.println( "in.close() unsuccessful"); } throw e; "#009900">// Rethrow } finally { // Don't close it here!!! } } String getLine() { String s; try { s = in.readLine(); } catch(IOException e) { System.err.println( "readLine() unsuccessful"); s = "failed"; } return s; } void cleanup() { try { in.close(); } catch(IOException e2) { System.err.println( "in.close() unsuccessful"); } } } public "#0000FF">class Cleanup { public "#0000FF">static "#0000FF">void main(String[] args) { try { InputFile in = new InputFile("#004488">"Cleanup.java"); String s; int i = 1; while((s = in.getLine()) != "#0000FF">null) System.out.println(""+ i++ + color="#004488">": " + s); in.cleanup(); } catch(Exception e) { System.err.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(System.err); } } } ///:~
|
 |
 |
 |
The constructor for InputFile
takes a String argument, which is the name of the file you want to open.
Inside a try block, it creates a FileReader using the file name. A
FileReader isn’t particularly useful until you turn around and use
it to create a BufferedReader that you can actually talk to—notice
that one of the benefits of InputFile is that it combines these two
actions.
|
 |
Le constructeur d'InputFile prend une String comme
paramètre, qui contient le nom du fichier que vous voulez ouvrir, à
l'intérieur du bloc try il crée un FileReader. Un
FileReader n'est pas particulièrement utile à moins que vous ne
le détourniez et l'utilisiez pour créer un BuffereReader avec
lequel vous puissiez communiquer, un des bénéfices de
l'InputFile est qu'il combine ses deux actions.
|
 |
 |
 |
If the FileReader constructor is
unsuccessful, it throws a
FileNotFoundException,
which must be caught separately because that’s the one case in which you
don’t want to close the file since it wasn’t successfully opened.
Any other catch clauses must close the file because it was opened
by the time those catch clauses are entered. (Of course, this is trickier if
more than one method can throw a FileNotFoundException. In that case, you
might want to break things into several try blocks.) The
close( ) method might throw an exception so it is tried and caught
even though it’s within the block of another catch
clause—it’s just another pair of curly braces to the Java compiler.
After performing local operations, the exception is rethrown, which is
appropriate because this constructor failed, and you wouldn’t want the
calling method to assume that the object had been properly created and was
valid.
|
 |
Si le constructeur de FileReader échoue il génère une
FileNotFoundException qui doit être interceptée séparément
puisque dans ce cas la vous ne voulez pas fermer le fichier qui n'a
pas été ouvert. Toute autre clause catch doit fermer le fichier
puisqu'il a été ouvert (bien sûr c'est plus délicat plus d'une
méthode peut générer une FileNotfoundException dans ce cas vous
pouvez vouloir séparer cela dans plusieurs blocs try).
La méthode close() doit lancer une exception ce qui est donc essayé et
capté bien que cela soit dans le bloc d'une autre clause catch - c'est
juste une autre paire d'accolades pour le compilateur Java. Après
l'exécution des opérations locales, l'exception est relancée, ce qui
est adéquat puisque le constructeur a échoué, et vous ne voudriez pas
que la méthode appelé ai a assumer que l'objet ai été correctement
crée et soit valide.
|
 |
 |
 |
In this example, which doesn’t use
the aforementioned flagging technique, the finally clause is definitely
not the place to close( ) the file, since that would close it
every time the constructor completed. Since we want the file to be open for the
useful lifetime of the InputFile object this would not be
appropriate.
|
 |
Dans cet exemple, qui n'utilise pas la technique du drapeau mentionnée
précédemment, la cause finally n'est définitivement pas la
place pour close() (ndt. fermer) le fichier, puisque cela
fermerait le fichier à chaque finalisation du constructeur. Comme nous
désirons que le fichier soit ouvert tout le temps de la durée de vie
de l'objet InputFile cela ne serait pas approprié.
|
 |
 |
 |
The getLine( ) method returns
a String containing the next line in the file. It calls
readLine( ), which
can throw an exception, but that exception is caught so getLine( )
doesn’t throw any exceptions. One of the design issues with
exceptions is whether to handle an exception completely
at this level, to handle it partially and pass the same exception (or a
different one) on, or whether to simply pass it on. Passing it on, when
appropriate, can certainly simplify coding. The getLine( ) method
becomes:
|
 |
La méthode getLine() renvoie un String contenant la prochaine
ligne du fichier. Elle appelle readLine(), qui peut lancer une
exception, mais cette exception est provoqué donc getLine() ne
lance pas d'exceptions. Une des finalités de la conception des
exceptions est soit de porter complètement une exception à ce niveau,
de la porter partiellement et de passer la même exception (ou une
différente), ou soit de la passer tout simplement. La passer, quand
c'est approprié, peut certainement simplifier le codage. La méthode
getLine() devient :
|
 |
 |
 |
String getLine() throws IOException { return in.readLine(); }
|
 |
String getLine() "#0000FF">throws IOException { return in.readLine(); }
|
 |
 |
 |
But of course, the caller is now
responsible for handling any IOException that might
arise.
|
 |
Mais bien sûr, l'appelant a maintenant la
responsabilité de porter toute IOException qui puisse
apparaître.
|
 |
 |
 |
The cleanup( ) method must be
called by the user when finished using the InputFile object. This will
release the system resources (such as file handles) that are used by the
BufferedReader and/or FileReader
objects[56]. You
don’t want to do this until you’re finished with the
InputFile object, at the point you’re going to let it go. You might
think of putting such functionality into a
finalize( ) method, but as mentioned in
Chapter 4 you can’t always be sure that finalize( ) will be
called (even if you can be sure that it will be called, you don’t
know when). This is one of the downsides to Java: all cleanup—other
than memory cleanup—doesn’t happen automatically, so you must inform
the client programmer that they are responsible, and possibly guarantee that
cleanup occurs using finalize( ).
|
 |
La méthode cleanUp() doit être appelée par
l'utilisateur lorsqu'il a fini d'utiliser l'objet InputFile().
Ceci relâchera les ressources système (comme les poignées de file) qui
étaient utilisées par les objets BufferedReader ou/et
FileReader. Vous ne voudrez pas faire cela tant que vous
n'aurez pas fini avec l'objet InputFile, au point de le laisser
partir. Vous devrez penser a mettre une fonctionnalité de ce type dans
une méthode finaliste(), mais comme il est mentionné dans le
Chapitre 4 vous ne pouvez pas toujours être sûr que finaliste() sera
appelé (même si vous pouvez être sûr de son appel, vous ne
savez pas quand). C'est un des à-cotés de Java : tout nettoyage
- autre que le nettoyage de la mémoire - ne se lance pas
automatiquement, donc vous devez informer le programmeur client qu'il
est responsable, et garantir au mieux que possible que le nettoyage
s'exécute par l'usage de finalise().
|
 |
 |
 |
In Cleanup.java an
InputFile is created to open the same source file that creates the
program, the file is read in a line at a time, and line numbers are added. All
exceptions are caught generically in main( ), although you could
choose greater granularity.
|
 |
Dans Cleanup.java un InputFile est
crée pour ouvrir le même fichier source qui crée le programme, le
fichier est lu une ligne à la fois, et les numéros de ligne sont
ajoutés. Toutes les exceptions sont causées génériquement dans
main(), si bien que vous pouvez choisir une plus grande
finesse.
|
 |
 |
 |
One of the benefits of this example is to
show you why exceptions are introduced at this point in the book—you
can’t do basic I/O without using exceptions. Exceptions are so integral to
programming in Java, especially because the compiler enforces them, that you can
accomplish only so much without knowing how to work with
them.
|
 |
Un des bénéfices de cet exemple est de vous
montrer pourquoi les exceptions sont abordés à ce point du livre -
vous ne pouvez pas faire de l'E/S [I/O] sans utiliser les exceptions.
Les exceptions sont tellement intégrés à la programmation en Java,
spécialement parce que le compilateur les demande, que vous pouvez
accomplir tellement peu de choses sans savoir comment travailler avec
elles.
|
 |
 |
 |
Exception matching
|
 |
Indication d'Exception
|
 |
 |
 |
When an exception is thrown, the
exception handling system looks through the “nearest” handlers in
the order they are written. When it finds a match, the exception is considered
handled, and no further searching occurs.
|
 |
Quand une exception est lancée, le système
d'indication des exceptions regarde dans les « plus proches »
identifiants dans l'ordre de leur écriture. Quand il trouve une
correspondance, l'exception est considérée comme identifiée, et aucune
autre recherche n'est lancée.
|
 |
 |
 |
Matching an exception doesn’t
require a perfect match between the exception and its handler. A derived-class
object will match a handler for the base class, as shown in
this example:
|
 |
Identifier une exception ne requiert pas de
correspondance exacte entre l'exception et sont identifiant. Une objet
de classe dérivée pourra correspondre à un identifiant de la classe de
base, comme montré dans cet exemple : "Index1125">
|
 |
 |
 |
//: c10:Human.java // Catching exception hierarchies.
class Annoyance extends Exception {} class Sneeze extends Annoyance {}
public class Human { public static void main(String[] args) { try { throw new Sneeze(); } catch(Sneeze s) { System.err.println("Caught Sneeze"); } catch(Annoyance a) { System.err.println("Caught Annoyance"); } } } ///:~
|
 |
//: c10:Human.java // Catching exception hierarchies. class Annoyance "#0000FF">extends Exception {} class Sneeze "#0000FF">extends Annoyance {} public "#0000FF">class Human { public "#0000FF">static "#0000FF">void main(String[] args) { try { throw "#0000FF">new Sneeze(); } catch(Sneeze s) { System.err.println("Caught Sneeze"); } catch(Annoyance a) { System.err.println("Caught Annoyance"); } } } ///:~
|
 |
 |
 |
The Sneeze exception will be
caught by the first catch clause that it matches—which is the first
one, of course. However, if you remove the first catch clause, leaving
only:
|
 |
L'exception Sneeze sera piégée par le
premier catch (ndt. cause) qui correspondra - qui sera le
premier, bien sûr. Cependant, si vous retirez la première cause du
piège, laissant seulement :
|
 |
 |
 |
try { throw new Sneeze(); } catch(Annoyance a) { System.err.println("Caught Annoyance"); }
|
 |
try { throw "#0000FF">new Sneeze(); } catch(Annoyance a) { System.err.println("Caught Annoyance"); }
|
 |
 |
 |
The code will still work because
it’s catching the base class of Sneeze. Put another way,
catch(Annoyance e) will catch an Annoyance or any class derived
from it. This is useful because if you decide to add more derived
exceptions to a method, then the client programmer’s code will not need
changing as long as the client catches the base class
exceptions.
|
 |
Le code sera toujours fonctionnel puisqu'il piège
la classe de base de Sneeze. Placé d'une autre manière,
catch(Annoyancee) identifiera une Annoyance ou tout
autre classe qui en est dérivée. C'est utile puisque si vous
décidez d'ajouter plus d'exceptions dérivées à une méthode, alors le
code du programmeur client n'aura pas besoin d'être modifié tant que
le client piégera les exceptions de la classe de base.
|
 |
 |
 |
If you try to “mask” the
derived-class exceptions by putting the base-class catch clause first, like
this:
|
 |
Si vous essayez de « masquer » les exceptions de
la classe dérivée en plaçant le piège de classe-de-base en premier,
comme ceci :
|
 |
 |
 |
try { throw new Sneeze(); } catch(Annoyance a) { System.err.println("Caught Annoyance"); } catch(Sneeze s) { System.err.println("Caught Sneeze"); }
|
 |
try { throw "#0000FF">new Sneeze(); } catch(Annoyance a) { System.err.println("Caught Annoyance"); } catch(Sneeze s) { System.err.println("Caught Sneeze"); }
|
 |
 |
 |
the compiler will give you an error
message, since it sees that the Sneeze catch-clause can never be
reached.
|
 |
le compilateur vous donnera un message d'erreur,
en voyant que la cause d'identification de Sneeze ne sera
jamais atteinte.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |