t
t
t
t
t t 10) Gestion des erreurs avec les exceptions
tttt
10) Gestion des erreurs avec les exceptions
Texte original t Traducteur : N. CULWELL-KANAREK, Anthony PRAUD, A. FORTUN
t
t
///
Ce chapitre contient 3 pages
1 2 3
\\\
t t t
t t t
t
t t t
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. t 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.
t t t
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. t 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.
t t t
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: t 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 :
t t t
//: 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();
  }
} ///:~
t
//: 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();
  }
} ///:~
t t t
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. t 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.
t t t
The output is: t La trace est :
t t t
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)
t
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)
t t t
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. t 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.
t t t
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: t 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.
t t t
  1. An error you cannot catch (receiving a null reference handed to your method by a client programmer, for example) .
  2. 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).
t
  1. Une erreur que vous ne pouvez pas intercepter( recevoir une référence transmise par un programme client avec la valeur null par exemple).
  2. Une erreur que vous auriez du prévoir dans votre code (comme ArrayIndexOutOfBoundsException ou vous auriez du faire attention à la taille du tableau).
t t t
You can see what a tremendous benefit it is to have exceptions in this case, since they help in the debugging process. t Vous pouvez voir quel bénéfice apportent les exceptions dans ce cas puisque cela aide le processus de déboguage.
t t t
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. t 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.
t t t

Performing cleanup with finally

t

Faire le ménage avec finally

t t t
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: t 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 :
t t t
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
}
t
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
}
t t t
To demonstrate that the finally clause always runs, try this program: t Pour démontrer que la clause finally est toujours exécutée lancer ce programme :
t t t
//: 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"
      }
    }
  }
} ///:~
t
//: 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"
      }
    }
  }
} ///:~
t t t
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. t 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.
t t t
The output is: t La sortie est :
t t t
ThreeException
In finally clause
No exception
In finally clause
t
ThreeException
In finally clause
No exception
In finally clause
t t t
Whether an exception is thrown or not, the finally clause is always executed. t Qu'une exception soit générée ou pas la clause finally est exécutée.
t t t

What’s finally for?

t

À Quoi sert le finally ?

t t t
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? t 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 ?
t t t
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: t 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 :
t t t
//: 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();
    }
  }
} ///:~
t
//: 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();
    }
  }
} ///:~
t t t
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: t 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 :
t t t
//: 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();
    }
  }
} ///:~
t
//: 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();
    }
  }
} ///:~
t t t
Here the sw.off( ) has been moved to just one place, where it’s guaranteed to run no matter what happens. t 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.
t t t
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: t 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.
t t t
//: 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");
    }
  }
} ///:~
t
//: 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");
    }
  }
} ///:~
t t t
The output for this program shows you what happens: t La sortie de ce programme vous montre ce qui arrive :
t t t
Entering first try block
Entering second try block
finally in 2nd try block
Caught FourException in 1st try block
finally in 1st try block
t
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
t t t
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. t 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.
t t t

Pitfall: the lost exception

t

Le défaut : l'exception perdue

t t t
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: t 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 :
t t t
//: 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();
    }
  }
} ///:~
t
//: 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();
    }
  }
} ///:~
t t t
The output is: t Le résultat est :
t t t
Exception in thread "main" A trivial exception
    at LostMessage.dispose(LostMessage.java:21)
at LostMessage.main(LostMessage.java:29)
t
Exception in thread "#004488">"main" A trivial exception
    at LostMessage.dispose(LostMessage.java:21)
    at LostMessage.main(LostMessage.java:29)
t t t
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). t 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()).
t t t

Exception restrictions

t

Restriction d'Exceptions

t t t
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. t 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.
t t t
This example demonstrates the kinds of restrictions imposed (at compile-time) for exceptions: t Cet exemple démontre les restrictions imposée (à la compilation) pour les exceptions :
t t t
//: 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");
    }
  }
} ///:~
t
//: 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");
    }
  }
} ///:~
t t t
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( ). t 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].
t t t
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. t 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.
t t t
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. t 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.
t t t
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. t 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.
t t t
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. t 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.
t t t
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]. t 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.
t t t
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. t 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.
t t t

Constructors

t

Les constructeurs

t t t
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. t 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.
t t t
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. t 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é.
t t t
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: t 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 :
t t t
//: 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);
    }
  }
} ///:~
t
//: 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);
    }
  }
} ///:~
t t t
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. t 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.
t t t
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. t 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.
t t t
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. t 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é.
t t t
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: t 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 :
t t t
String getLine() throws IOException {
  return in.readLine();
}
t
String getLine() "#0000FF">throws IOException {
  return in.readLine();
}
t t t
But of course, the caller is now responsible for handling any IOException that might arise. t Mais bien sûr, l'appelant a maintenant la responsabilité de porter toute IOException qui puisse apparaître.
t t t
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( ). t 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().
t t t
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. t 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.
t t t
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. t 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.
t t t

Exception matching

t

Indication d'Exception

t t t
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. t 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.
t t t
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: t 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">
t t t
//: 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");
    }
  }
} ///:~
t
//: 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");
    }
  }
} ///:~
t t t
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: t 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 :
t t t
    try {
      throw new Sneeze();
    } catch(Annoyance a) {
      System.err.println("Caught Annoyance");
}
t
try {
      throw "#0000FF">new Sneeze();
    } catch(Annoyance a) {
      System.err.println("Caught Annoyance");
    }
t t t
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. t 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.
t t t
If you try to “mask” the derived-class exceptions by putting the base-class catch clause first, like this: t 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 :
t t t
    try {
      throw new Sneeze();
    } catch(Annoyance a) {
      System.err.println("Caught Annoyance");
    } catch(Sneeze s) {
      System.err.println("Caught Sneeze");
}
t
try {
      throw "#0000FF">new Sneeze();
    } catch(Annoyance a) {
      System.err.println("Caught Annoyance");
    } catch(Sneeze s) {
      System.err.println("Caught Sneeze");
    }
t t t
the compiler will give you an error message, since it sees that the Sneeze catch-clause can never be reached. t le compilateur vous donnera un message d'erreur, en voyant que la cause d'identification de Sneeze ne sera jamais atteinte.
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel