IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java  -  Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières  -  Thinking in Java

  Chapitre 10 - Gestion des erreurs avec les exceptions

pages : 1 2 3

Cela peut paraître effrayant de penser que l'on devra vérifier le null pour chaque référence passée à une 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.
  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.
  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();
  }
} ///:~

  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.
  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)

  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.
  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.
 
  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).

  Vous pouvez voir quel bénéfice apportent les exceptions dans ce cas puisque cela aide le processus de déboguage.
  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.
 

Faire le ménage avec finally


  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
}

  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"
      }
    }
  }
} ///:~

  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.
  La sortie est :
 
ThreeException
In finally clause
No exception
In finally clause

  Qu'une exception soit générée ou pas la clause finally est exécutée.
 

À Quoi sert le finally ?


  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 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();
    }
  }
} ///:~

  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();
    }
  }
} ///:~

  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.
  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");
    }
  }
} ///:~

  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

  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.
 

Le défaut : l'exception perdue


  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();
    }
  }
} ///:~

  Le résultat est :
 
Exception in thread  "main" A trivial exception
    at LostMessage.dispose(LostMessage.java:21)
    at LostMessage.main(LostMessage.java:29)

  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()).
 

Restriction d'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.
  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");
    }
  }
} ///:~

  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].
  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.
  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.
  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.
  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.
  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.
  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.
 

Les constructeurs


  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.
  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é.
  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++ + color= ": " + s);
      in.cleanup();
    } catch(Exception e) {
      System.err.println(
        "Caught in main, e.printStackTrace()");
      e.printStackTrace(System.err);
    }
  }
} ///:~

  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.
  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.
  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é.
  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();
}

  Mais bien sûr, l'appelant a maintenant la responsabilité de porter toute IOException qui puisse apparaître.
  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().
  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.
  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.
 

Indication d'Exception


  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.
  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");
    }
  }
} ///:~

  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");
    }

  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.
  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");
    }

  le compilateur vous donnera un message d'erreur, en voyant que la cause d'identification de Sneeze ne sera jamais atteinte.

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Armel Fortun ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3
Penser en Java  -  Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières  -  Thinking in Java