Cet article se concentre sur certains malentendus dans la sélection et l'utilisation des exceptions Java. J'espère que les lecteurs pourront maîtriser certains des points et principes de la gestion des exceptions et faire attention au résumé et à l'induction. Ce n'est qu'en gérant les exceptions que nous pouvons améliorer les qualités de base des développeurs, améliorer la robustesse du système, améliorer l'expérience utilisateur et améliorer la valeur du produit.
Malentendu 1. Choix anormal
Figure 1. Classification des anomalies
La figure 1 décrit la structure de l'exception. En fait, nous savons tous que les anomalies sont divisées en détection des anomalies et des anomalies non détectées, mais dans la pratique, l'application de ces deux anomalies est confuse. Parce que les exceptions non détectées sont faciles à utiliser, de nombreux développeurs pensent que la détection des exceptions est inutile. En fait, les scénarios d'application anormaux peuvent être résumés comme suit:
1. Le code d'appel ne peut pas continuer à être exécuté et doit être résilié immédiatement. Il y a trop de possibilités pour cette situation, comme le serveur n'est pas connecté, les paramètres sont incorrects, etc. Les exceptions non détectées sont applicables à ces moments, et il n'est pas nécessaire d'appeler la capture et le traitement explicites du code, et le code est concis et clair.
2. Le code d'appel a besoin d'un traitement et d'une récupération ultérieurs. Si SQLEXception est définie comme des exceptions non détectées, les développeurs croiront naturellement que SQLEXception ne nécessite pas d'appeler la capture explicite et le traitement du code, ce qui conduira à des situations sérieuses telles que ne pas fermer la connexion, ne pas faire reculer la transaction et les données sales dans la base de données. C'est précisément parce que SQLEXception est définie comme détectant des exceptions que les développeurs seront poussés à capturer et à nettoyer explicitement les ressources après que le code a généré une exception. Bien sûr, après avoir nettoyé les ressources, vous pouvez continuer à lancer des exceptions non détectées pour empêcher l'exécution du programme. Selon l'observation et la compréhension, la détection des exceptions peut être principalement appliquée aux classes d'outils. Java Learning Group 669823128
Misonctant 2: Afficher les exceptions directement sur la page ou le client.
Il est courant d'imprimer des exceptions directement du côté client. En prenant JSP comme exemple, une fois que le code s'exécute, le conteneur imprime les informations de pile d'exception directement sur la page par défaut. En fait, du point de vue du client, toute exception n'a aucune signification pratique, et la plupart des clients ne peuvent pas comprendre les informations d'exception. Le développement de logiciels devrait également essayer d'éviter de présenter l'exception directement aux utilisateurs.
Listing 1
<strong du package </strong> com.ibm.dw.sample.exception; / ** * coutume RuntimeException * Ajouter un attribut de code d'erreur * / <strong> public </strong> <strong> classe </strong> <strong> runtimeException </strong> <strong> étend </strong> <strong> java </strong>. <strong> Lang </strong>. <strong> Runtimexception </strong>. <strong> public </strong> <strong> statique </strong> <strong> final </strong> entier générique = 1000000; // Code d'erreur <strong> privé </strong> entier d'erreur entier; <strong> public </strong> <strong> runtimeException </strong> (entier d'erreur entier, cause thrognable) {<strong> this </strong> (errorcode, <strong> null </strong>, cause); } <strong> public </strong> <strong> runtimeException </strong> (message de chaîne, cause thordivable) {// Utilisez le code d'erreur général <strong> this </strong> (générique, message, cause); } <strong> public </strong> <strong> runtimeException </strong> (entier d'erreur entier, message de chaîne, cause thrognable) {<strong> super </strong> (message, cause); <strong> this </strong> .errorcode = errorcode; } <strong> public </strong> entier <strong> getErrorCode </strong> () {<strong> return </strong> errorcode; }}Comme le montre l'exemple de code, introduisez des codes d'erreur dans des exceptions. Une fois qu'une exception se produit, nous présentons simplement le code d'erreur de l'exception à l'utilisateur, ou convertissons le code d'erreur en une invite plus compréhensible. En fait, le code d'erreur ici contient également une autre fonction, et les développeurs peuvent également savoir avec précision quel type d'exception s'est produit en fonction du code d'erreur.
Idée à fautes 3: Pollution de la hiérarchie du code
Nous divisons souvent le code en différentes hiérarchies telles que le service, la logique métier, le DAO, etc. La couche DAO contiendra des méthodes de lancer des exceptions, comme indiqué dans Listing 2:
Listing 2
<strong> public </strong> Client <strong> RetrieVECustomerById </strong> (Long ID) <strong> Throw </strong> sqlexception {// interroge la base de données basée sur ID}À première vue, il n'y a aucun problème avec le code ci-dessus, mais si vous pensez attentivement du point de vue du couplage de conception, la SQLEXception pollue ici le code d'appel supérieur. La couche d'appel doit utiliser explicitement les coups de main pour capturer, ou le jeter au niveau supérieur. Selon le principe de l'isolement de conception, nous pouvons le modifier de manière appropriée en:
Listing 3
<strong> public </strong> Client <strong> RetrieVECustomerById </strong> (Long ID) {<strong> try </strong> {// interroger la base de données basée sur ID} <strong> capture </strong> (SQLEXception E) {// Utiliser une encapsulation d'exception non détected pour détecter les exceptions, réduire le couplage hiérarchique <strong> Throw </strong> RuntimeException (sqlerrorcode, e); } <strong> Enfin </strong> {// Fermez la connexion et nettoie les ressources}}Idée à fautes 4: ignorer les exceptions
La gestion d'exception suivante ne fait que sortir l'exception à la console, et cela n'a aucun sens. De plus, une exception apparaît ici et le programme ne s'interrompt pas, et le code d'appel continue d'être exécuté, ce qui entraîne plus d'exceptions.
Listing 4
<strong> public </strong> <strong> void </strong> <strong> récupérerObjectById </strong> (ID long) {<strong> try </strong> {//..some code qui lance Sqlexception} <strong> capture </strong> (sqlexception ex) {/ ** * qui qui sait le console. * Dans l'environnement de production, la pile d'erreur doit être sortie dans le journal. * Et le programme continue de s'exécuter après le processus de capture, ce qui entraînera d'autres problèmes * / ex.printStackTrace (); }}Peut être reconstruit:
Listing 5
<strong> public </strong> <strong> void </strong> <strong> récupérerObjectById </strong> (ID long) {<strong> try </strong> {//..some code qui lance sqlexception} <strong> capture </strong> (sqlexception ex) {<strong> lancent </strong> </strong> ", runtimeException (" exception </ Strong> » ex); } <strong> Enfin </strong> {// nettoyer les résultats, instruction, connexion, etc.}Ce malentendu est relativement basique et dans des circonstances normales, vous ne ferez pas cette erreur de bas niveau.
Idée à fautes 5: Inclure des exceptions dans les blocs d'énoncé de boucle
Comme indiqué dans le code suivant, l'exception est contenue dans le bloc d'instructions de boucle FOR.
Listing 6
<strong> pour </strong> (<strong> int </strong> i = 0; i <100; i ++) {<strong> essayez </strong> {} <strong> capture </strong> (xxxException e) {//…. }}Nous savons tous que la gestion des exceptions occupe des ressources système. À première vue, tout le monde pensait qu'ils ne feraient pas une telle erreur. Dans une autre perspective, une boucle est exécutée dans la classe A, et la méthode de classe B est appelée dans la boucle, mais la méthode appelée dans la classe B contient des blocs d'instructions tels que l'essai. La hiérarchie de la classe s'est estompée et le code est exactement le même que ci-dessus.
Idée de confusion 6: Utilisez une exception pour capturer toutes les exceptions potentielles
Plusieurs types d'exceptions différents sont lancés lors de l'exécution d'une méthode. Par souci de simplicité du code, l'exception de la classe de base est utilisée pour assister à toutes les exceptions potentielles, comme indiqué dans l'exemple suivant:
Listing 7
<strong> public </strong> <strong> void </strong> <strong> retrieveObjectByid </strong> (ID long) {<strong> essayez </strong> {//… appelez le code qui lance ioException //… appelez le code qui lance Sqlexception} <strong> capture </strong> (exception e) {// ici toutes les exceptions potentielles capturées par l'exception de la classe de base. Si plusieurs niveaux le captent, les informations valides de l'exception d'origine seront perdues <strong> lancez </strong> <strong> nouveau </strong> runtimeException ("Exception <strong> dans </strong> rétriveObjectByid", e); }}Peut être reconstruit
Listing 8
<strong> public </strong> <strong> void </strong> <strong> récupérerObjectById </strong> (Id long) {<strong> try </strong> {//..some code qui lance RuntimeException, ioException, sqlexception} <strong> capture </strong> (ioException e) {// simplement catch ioException <strong> throw RuntimeException (/ * Spécifiez le code d'erreur correspondant à ioException ici * / code, "Exception <strong> dans </strong> rétrieveObjectByid", e); } <strong> catch </strong> (sqlexception e) {// catch sqlexception <strong> throw </strong> <strong> new </strong> runtimeException (/ * spécifie le code d'erreur correspondant à sqlexception ici * / code, "exception <strong> dans </strong> retrrieveObjectByid", e); }}Idée fausse 7: L'encapsulation à plusieurs niveaux lance des exceptions non détectées
Si nous insistons toujours que différents types d'exceptions doivent utiliser différentes instructions de capture, la plupart des exemples peuvent contourner cette section. Cependant, si un seul morceau d'appels de code lancera plus d'une exception, il n'est souvent pas nécessaire d'écrire une instruction Catch pour chaque type d'exception différent. Pour le développement, toute exception est suffisante pour expliquer le problème spécifique du programme.
Listing 9
<strong> essayez </strong> {// La RuntimeException, IOEXEPtion ou d'autres peuvent être lancées; // Notez la différence entre ici et les malentendus six, voici un morceau de code qui lance plusieurs exceptions. Ce qui précède est plusieurs segments de code, chacun lançant différentes exceptions} <strong> catch </strong> (<strong> exception </strong> e) {// Comme toujours, convertissant l'exception en RuntimeException, mais l'E est en fait une instance de RuntimeException, et a été encapsulé dans le code précédent. RuntimeException (/ ** / code, / ** /, e);}Si nous convertissons toutes les exceptions en RuntimeException comme indiqué dans l'exemple ci-dessus, alors lorsque le type d'exception est déjà RuntimeException, nous faisons une autre encapsulation. Le RuntimeException a été de nouveau à nouveau à nouveau, et les informations valides transportées par la RuntimeException originale ont été perdues.
La solution est que nous pouvons ajouter des vérifications pertinentes à la classe RuntimeException pour confirmer que le paramètre Thrownable n'est pas une instance de RuntimeException. Si c'est le cas, copiez l'attribut correspondant à l'instance nouvellement créée. Ou utilisez différents blocs de déclaration de capture pour attraper RuntimeException et autres exceptions. Méthode de préférence personnelle 1, les avantages sont évidents.
Idée à fautes 8: exception d'impression à plusieurs niveaux
Examinons d'abord l'exemple suivant, qui définit les 2 classes A et B. Le code de classe B est appelé dans la classe A, et les exceptions de capture et d'impression de classe A et de classe B.
Listing 10
<strong> public </strong> <strong> classe </strong> <strong> a </strong> {<strong> private </strong> <strong> static </strong> logger Logger = loggerFactory.getLogger (a.class); <strong> public </strong> <strong> void </strong> <strong> process </strong> () {<strong> try </strong> {// classe B instancielle, vous pouvez passer à d'autres méthodes d'injection telles que b b = <strong> nouveau </strong> b (); B.Process (); // Autre code peut entraîner une exception} <strong> catch </strong> (xxxException e) {// Si la méthode de processus de classe B lance une exception, l'exception sera en b, la classe est imprimée, et elle sera également imprimée ici, donc logger.error (e); <strong> throw </strong> <strong> nouveau </strong> runtimeException (/ * Code d'erreur * / errorcode, / * Informations d'exception * / msg, e); }}} <strong> public </strong> <strong> classe </strong> <strong> b </strong> {<strong> privé </strong> <strong> static </strong> logger Logger = loggerfactory.getLogger (b.class); <strong> public </strong> <strong> void </strong> <strong> processus </strong> () {<strong> try </strong> {// code qui peut lancer l'exception} <strong> catch </strong> (xxxException e) {logger.error (e); <strong> throw </strong> <strong> nouveau </strong> runtimeException (/ * Code d'erreur * / errorcode, / * Informations d'exception * / msg, e); }}}La même exception sera imprimée 2 fois. Si le niveau est un peu plus compliqué, c'est un mal de tête de ne pas considérer les performances du système des journaux d'impression et de localiser simplement des problèmes spécifiques dans les journaux d'exception.
En fait, les journaux d'impression ne nécessitent que la capture et l'impression à la couche la plus externe du code. L'impression d'exception peut également être écrite comme AOP et tissée dans la couche la plus externe du cadre.
Idé conception 9: Le problème que les informations contenues dans les exceptions ne peuvent pas être entièrement situées
Les exceptions permettent non seulement aux développeurs de savoir ce qui ne va pas, mais plus souvent, les développeurs ont également besoin de savoir ce qui cause le problème. Nous savons que Java .lang.Exception a un constructeur de paramètres de type de chaîne, et cette chaîne peut être personnalisée en informations rapides faciles à comprendre.
Les développeurs d'informations personnalisées simples ne peuvent savoir où l'exception apparaît, mais dans de nombreux cas, les développeurs doivent en savoir plus sur les paramètres qui provoquent une telle exception. À l'heure actuelle, nous devons ajouter les informations des paramètres de l'appel de méthode aux informations personnalisées. L'exemple suivant ne répertorie que le cas d'un paramètre. Dans le cas de plusieurs paramètres, vous pouvez écrire une classe d'outils pour organiser une telle chaîne.
Listing 11
public <strong> void </strong> rétrieveObjectById (ID long) {<strong> try </strong> {//..some code qui lance sqlexception} <strong> capture </strong> (sqlexception ex) {// ajouter des informations de paramètre à des informations d'exception <strong> lancez </strong> rétrontable </strong> runtimeexception ("exception <strong> <strong> avec </strong> id objet: "+ id, ex); }}Idée à fautes 10: Impossible de prédire les anomalies potentielles
Pendant le processus d'écriture du code, en raison du manque de compréhension approfondie du code d'appel, il est impossible de déterminer avec précision si le code appelé produira des exceptions, donc le traitement est ignoré. Une fois un bug de production généré, je me suis souvenu que je devais ajouter une récupération d'exception à un certain morceau de code, et je ne pouvais même pas signaler avec précision la cause de l'exception. Cela nécessite que les développeurs savent non seulement ce qu'ils font, mais aussi pour savoir autant que possible ce que les autres ont fait et quels résultats possibles peuvent être provoqués, et prendre en compte le processus de traitement de l'ensemble de l'application dans une perspective globale. Ces idées affecteront notre écriture et notre traitement du code.
Idée fausse 11: Utilisation mitigée de plusieurs bibliothèques de journaux tiers
De nos jours, il y a de plus en plus de bibliothèques de journaux tiers Java. Divers cadres seront introduits dans un grand projet, et ces cadres s'appuieront sur l'implémentation de différentes bibliothèques de journaux. Le problème le plus gênant est de ne pas introduire toutes les bibliothèques de journaux nécessaires, le problème est que les bibliothèques de journaux introduites sont elles-mêmes incompatibles. S'il peut être facile de résoudre au début du projet, vous pouvez réintroduire toutes les bibliothèques de journaux de votre code au besoin ou passer à un cadre. Mais ce type de coût n'est pas abordable pour chaque projet, et plus le risque est grand à mesure que le projet progresse.
Comment pouvons-nous éviter efficacement des problèmes similaires? La plupart des cadres ont maintenant pris en compte des problèmes similaires. Ils peuvent configurer des propriétés ou des fichiers XML, des paramètres ou des classes d'implémentation de journaux d'analyse d'exécution dans la bibliothèque Lib, et ce n'est que lorsque l'application est en cours d'exécution peut déterminer la bibliothèque de journaux spécifique à appliquer.
En fait, sur la base du principe de ne pas nécessiter plusieurs niveaux d'impression logarithmique, nous pouvons simplifier de nombreuses classes qui ont initialement appelé le code d'impression logarithmique. Dans de nombreux cas, nous pouvons utiliser des intercepteurs ou des filtres pour imprimer des journaux pour réduire le coût de la maintenance et de la migration du code.
Conclusion
Ce qui précède est une expérience et un résumé purement personnels. Les choses sont dialectiques et il n'y a pas de principes absolus. Le principe le plus efficace vous convient. J'espère que l'explication et l'analyse ci-dessus pourront vous être utiles.