La raison pour laquelle les exceptions sont une méthode de débogage puissante est qu'ils répondent aux trois questions suivantes:
1. Qu'est-ce qui ne va pas?
2. Où avez-vous fait une erreur?
3. Pourquoi quelque chose a-t-il mal tourné?
Dans le cas d'une utilisation efficace des exceptions, le type d'exception répond à "ce qui" est lancé, la trace de pile d'exception répond "où" est jetée, les informations d'exception répondent "pourquoi" est jetée, et si votre exception ne répond pas à toutes les questions ci-dessus, alors vous ne les utilisez peut-être pas bien.
Il existe trois principes qui peuvent vous aider à maximiser l'utilisation de bonnes exceptions lors du débogage. Ces trois principes sont:
1. spécifique et clair
2. Jetez-le tôt
3. Capture de retard
Afin d'expliquer ces trois principes de gestion efficace des exceptions, cet article en discute en fabriquant un directeur financier personnel JCheckbook, qui est utilisé pour enregistrer et suivre les activités du compte bancaire telles que les dépôts et les retraits et l'émission de factures.
Béton et clair
Java définit une hiérarchie d'une classe d'exception, qui commence par le jetable, étend une erreur et une exception, et l'exception étend RuntimeException. Comme le montre la figure 1.
Ces quatre classes sont généralisées et ne fournissent pas beaucoup d'informations d'erreur. Bien que l'instanciation de ces classes soit syntaxiquement légale (comme: new Throwable ()), il est préférable de les traiter comme des classes de base virtuelles et d'utiliser leurs sous-classes plus spécialisées. Java a fourni un grand nombre de sous-classes d'exception. Si vous souhaitez être plus précis, vous pouvez également définir votre propre classe d'exception.
Par exemple: le package java.io définit la sous-classe de la classe d'exception IOException, qui est plus spécialisée et la sous-classe d'iOException telle que filenotfoundException, eofException et objectStreamException. Chacun décrit une classe spécifique d'erreurs d'E / S: perte de fichier, fin de fichier d'exception et erreurs de flux d'objets sérialisés. Plus l'exception est spécifique, notre programme sera en mesure de répondre à la question "ce qui n'a pas fonctionné".
Il est également important d'essayer d'être aussi clair que possible lors de la capture d'exceptions. Par exemple: JCheckBook peut gérer FileLoTFoundException en requête le nom de fichier de l'utilisateur. Pour EofException, il peut continuer à s'exécuter en fonction des informations lues avant que l'exception ne soit lancée. Si un objetStreAmException est lancé, le programme doit inviter l'utilisateur que le fichier est corrompu et qu'un fichier de sauvegarde ou un autre fichier doit être utilisé.
Java facilite la capture explicite des exceptions, car nous pouvons définir plusieurs blocs de capture pour le même bloc d'essai, afin que chaque exception puisse être gérée de manière appropriée séparément.
Fichier prefsfile = nouveau fichier (prefsFileName); essayez {readPreferences (prefsfile);} catch (filenotfoundException e) {// alerter l'utilisateur que le fichier spécifié // n'existe pas} capture (eofException e) {// alerte l'utilisateur que la fin du fichier // a été atteint} capture (objectStreamException e) {// alerte le fichier que le fichier est corrompu} Catch (ioexection e) l'utilisateur qu'une autre erreur d'E / S s'est produite}JCheckBook fournit aux utilisateurs des informations explicites sur la capture des exceptions en utilisant plusieurs blocs de capture. Par exemple: si filenotfoundException est capturé, il peut inciter l'utilisateur à spécifier un autre fichier. Dans certains cas, l'effort d'encodage supplémentaire introduit par plusieurs blocs de capture peut être un fardeau non essentiel, mais dans cet exemple, le code supplémentaire aide le programme à fournir une réponse plus conviviale.
En plus des exceptions gérées par les trois premiers blocs de capture, le dernier bloc de capture fournit à l'utilisateur des informations d'erreur plus généralisées lorsque l'iOException est lancé. De cette façon, le programme peut fournir autant que possible des informations spécifiques, mais a également la capacité de gérer d'autres exceptions inattendues.
Parfois, les développeurs captent des exceptions normalisées et affichent le nom de classe des exceptions ou impriment des informations de pile pour "spécificité". Ne fais pas ça! Les utilisateurs n'auront mal à la tête que lorsqu'ils verront java.io.EOFException ou Stack Informations, plutôt que d'obtenir de l'aide. Des exceptions spécifiques doivent être capturées et l'utilisateur doit être invité avec des "mots humains". Cependant, la pile d'exception peut être imprimée dans votre fichier journal. N'oubliez pas que les exceptions et les informations de pile sont utilisées pour aider les développeurs plutôt que les utilisateurs.
Enfin, il convient de noter que JCheckBook n'attrape pas des exceptions dans readPreferences() , mais laisse les exceptions de capture et de gestion à la couche d'interface utilisateur, afin que les utilisateurs puissent être informés à l'aide de boîtes de dialogue ou d'autres méthodes. C'est ce qu'on appelle la "capture de retard", qui sera discutée ci-dessous.
Jeter tôt
Informations sur la pile d'exception fournit l'ordre exact de la chaîne d'appels de méthode qui provoque l'exception, y compris le nom de classe, le nom de la méthode, le nom du fichier de code et même le numéro de ligne de chaque appel de méthode, afin de localiser avec précision la scène où l'exception se produit.
java.lang.nullpointerExceptionat java.io.fileInputStream.open (méthode native) sur java.io.fileinputStream. <IniT> (fileinputstream.java:103) à jcheckbook.jcheckbook.readpreferences (jcheckbook.java:225) jcheckbook.jcheckbook.startup (jcheckbook.java:116) sur jcheckbook.jcheckbook. <init> (jcheckbook.java:27) sur jcheckbook.jcheckbook.main (jcheckbook.java:318)
Ce qui précède montre le cas où la méthode ouverte () de la classe FileInputStream lance une nulpointerException. Cependant, notez que FileInputStream.close() fait partie de la bibliothèque de classe Java standard, et il est probable que le problème de cette exception est que notre code lui-même n'est pas l'API Java. Le problème est donc susceptible d'apparaître dans l'une des méthodes précédentes, et heureusement, elle est également imprimée dans les informations de la pile.
Malheureusement, NullPointerException est l'exception la moins informative (mais aussi la plus courante et la plus écrasante) en Java. Il ne mentionne pas ce dont nous nous soucions le plus: où est nul. Nous avons donc dû faire quelques pas en arrière et découvrir où quelque chose s'est mal passé.
En étapes, en soutenant les informations de la pile de suivi et en vérifiant le code, nous pouvons déterminer que la cause de l'erreur est qu'un paramètre de nom de fichier vide a été transmis dans readPreferences() . Étant donné que readPreferences() sait qu'il ne peut pas gérer les noms de fichiers vides, vérifiez immédiatement la condition:
public void readPreferences (String FileName) lève illégalArgumentException {if (filename == null) {lancez new illégalargumentException ("le nom de fichier est nul"); } // if //...perform d'autres opérations ... inputStream dans = new FileInputStream (nom de fichier); //..Read le fichier de préférences ...} En lançant une exception plus tôt (également connue sous le nom de «défaillance rapidement»), l'exception est claire et exacte. Les informations de pile reflètent immédiatement ce qui n'a pas fonctionné (des valeurs de paramètres illégales ont été fournies), pourquoi elle a mal tourné (le nom de fichier ne peut pas être nul) et où il a mal tourné (la première partie de readPreferences() ). De cette façon, nos informations de pile peuvent être fournies honnêtement:
Java.lang.ILLEGALARGUMENTException: le nom de fichier est nullat jcheckbook.jcheckbook.readpreferences (jcheckbook.java:207) à jcheckbook.jcheckbook.startup (jcheckbook.java:116) sur jcheckbook.jcheckbook. jcheckbook.jcheckbook.main (jcheckbook.java:318)
En outre, les informations d'exception contenues ("Le nom du fichier est vide") rend l'exception plus riche en répondant explicitement à la question de ce qui est vide, qui ne nous est pas disponible dans le code précédent.
L'échec est réalisé en lançant une exception immédiatement lorsqu'une erreur est détectée, la construction d'objets inutile ou l'utilisation des ressources, telle que la connexion de fichiers ou de réseau, peut être effectivement évitée. De même, les opérations de nettoyage provoquées par l'ouverture de ces ressources peuvent être enregistrées.
Capture retardée
Une erreur que les novices et les maîtres peuvent faire est d'attraper le programme avant d'avoir la possibilité de gérer l'exception. Le compilateur Java facilite indirectement ce comportement en exigeant que l'exception vérifiée soit capturée ou jetée. Le moyen naturel est d'envelopper immédiatement le code dans un bloc d'essai et d'utiliser Catch pour attraper des exceptions pour éviter l'erreur du compilateur.
La question est, que dois-je faire si j'obtiens l'exception après avoir été capturée? La pire chose à faire est de ne rien faire. Un bloc de capture vide équivaut à lancer toute l'exception dans le trou noir, et toutes les informations qui peuvent expliquer quand, où, pourquoi les erreurs sont erronées seront perdues pour toujours. Il est un peu mieux d'écrire des exceptions dans le journal, au moins il y a des enregistrements à vérifier. Mais nous ne pouvons pas nous attendre à ce que les utilisateurs lisent ou comprennent les fichiers journaux et les informations d'exception. Il n'est pas non plus approprié que readPreferences() affiche une boîte de dialogue de message d'erreur, car bien que JCheckBook soit actuellement une application de bureau, nous prévoyons également de le transformer en une application Web basée sur HTML. Dans ce cas, l'affichage d'une boîte de dialogue d'erreur n'est évidemment pas une option. Dans le même temps, quelle que soit la version HTML ou C / S, les informations de configuration sont lues sur le serveur et le message d'erreur doit être affiché dans le navigateur Web ou le programme client. readPreferences() devrait également prendre en considération ces besoins futurs lors de la conception. La séparation appropriée du code d'interface utilisateur et de la logique du programme peut améliorer la réutilisation de notre code.
Avoir une exception prématurément avant la manipulation conditionnelle, cela entraîne souvent des erreurs plus graves et d'autres exceptions. Par exemple, si readPreferences() ci-dessus capture et enregistre immédiatement la FileLoTFoundException qui peut être lancée lorsque le constructeur FileInputStream est appelé, le code deviendra comme ceci:
public void readPreferences (String filename) {// ... inputStream dans = null; // ne faites pas cela !!! essayez {in = new FileInputStream (filename);} catch (filenotfoundException e) {logger.log (e);} in.read (...); // ...}Le code ci-dessus le capture sans la capacité de se remettre de filenotfoundException. Si le fichier ne peut être trouvé, la méthode suivante ne peut évidemment pas la lire. Que se passe-t-il si readPreferences () est invité à lire un fichier qui n'existe pas? Bien sûr, FileNotFoundException sera enregistré, et si nous examinions le fichier journal à ce moment-là, nous le saurions. Cependant, que se passe-t-il lorsqu'un programme essaie de lire les données d'un fichier? Étant donné que le fichier n'existe pas, la variable en est vide et une nulpointerException sera lancée.
Lors du débogage d'un programme, l'instinct nous dit de regarder les informations à la fin du journal. Ce serait nullpointerException, et ce qui est très ennuyeux, c'est que cette exception est très peu spécifique. Le message d'erreur nous induit non seulement ce qui a mal tourné (l'erreur réelle est filenotfoundException au lieu de NullPointerException), mais induit également en erreur la source de l'erreur. Le vrai problème est en dehors du nombre de lignes à la NullPointerException lancée, qui peut avoir plusieurs appels de méthode et destruction de classe. Notre attention a été tirée de la véritable erreur par les petits poissons jusqu'à ce que nous regardions le journal pour trouver la source du problème.
Puisque ce que readPreferences() devrait vraiment faire est de ne pas attraper ces exceptions, quelle devrait être? Cela semble un peu contre-intuitif, et le moyen le plus approprié est de ne rien faire et de ne pas prendre des exceptions immédiatement. Laissez la responsabilité à l'appelant de readPreferences() et laissez-le étudier la façon appropriée de gérer les fichiers de configuration manquants. Il peut inciter l'utilisateur à spécifier d'autres fichiers ou à utiliser la valeur par défaut. Si cela ne fonctionne vraiment pas, cela peut avertir l'utilisateur et quitter le programme.
La façon de transmettre la responsabilité de la gestion des exceptions en amont de la chaîne d'appels est de déclarer l'exception dans la clause de lancers de la méthode. Lorsque vous déclarez des exceptions possibles, soyez prudent, plus le mieux est spécifique. Ceci est utilisé pour identifier le type d'exception que le programme appelant votre méthode doit connaître et être prêt à gérer. Par exemple, la version "Delay Capture" de readPreferences() pourrait ressembler à ceci:
public void readPreferences (String FileName) lève illégalArgumentException, filenotFoundException, ioException {if (filename == null) {throw new illégalArgumentException ("filename est nul"); } // if // ... inputStream dans = new FileInputStream (nom de fichier); // ...}Techniquement, la seule exception que nous devons déclarer est une IOException, mais nous déclarons explicitement que la méthode peut lancer une FileLoTFoundException. Il n'est pas nécessaire de déclarer illégalargumentException car il s'agit d'une exception non cocher (c'est-à-dire une sous-classe de RuntimeException). Cependant, il est déclaré pour documenter notre code (ces exceptions doivent également être marquées dans les Javadocs de la méthode).
Bien sûr, finalement, votre programme doit prendre l'exception, sinon il se terminera de façon inattendue. Mais l'astuce ici est de prendre des exceptions au bon niveau afin que votre programme puisse soit se remettre de manière significative des exceptions et continuer sans provoquer des erreurs plus profondes; ou être en mesure de fournir aux utilisateurs des informations claires, notamment en les guidant pour se remettre des erreurs. Si votre méthode n'est pas compétente, ne gérez pas l'exception, laissez-la derrière pour l'attraper et le gérer au bon niveau.
Résumer
Les développeurs expérimentés savent que la plus grande difficulté de débogage des programmes n'est pas de corriger les défauts, mais de découvrir les cachettes des défauts d'une quantité massive de code. Tant que vous suivez les trois principes de cet article, vos exceptions peuvent vous aider à suivre et à éliminer les défauts, à rendre votre programme plus robuste et convivial. Ce qui précède est l'intégralité du contenu de cet article, et j'espère que cela vous aidera dans votre étude ou votre travail.