web-dev-qa-db-fra.com

Pourquoi Java les gens consomment fréquemment les exceptions en silence?

Je n'ai jamais fait de sérieux Java codage auparavant, mais j'ai appris la syntaxe, les bibliothèques et les concepts basés sur mes compétences existantes (Delphi & C #). Une chose que je comprends à peine, c'est que j'ai vu tellement de code qui consomme silencieusement des exceptions après printStackTrace comme ceci:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

Il y a du code similaire comme celui-ci dans presque tous les Java que j'ai rencontrés. D'après mes connaissances, c'est très mauvais. L'exception devrait presque toujours être transmise au contexte externe comme ceci:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

La plupart du temps, l'exception devrait finir par être gérée au niveau de la boucle la plus externe qui appartient au framework sous-jacent (Java Swing par exemple). Pourquoi cela ressemble-t-il à la norme de coder comme ça dans le monde Java? Je suis perplexe.

Sur la base de mes antécédents, je préfère supprimer printStackTrace entièrement. Je renverrais simplement comme un _ RuntimeException non géré (ou, mieux encore, AssertionError), puis le capturerais et le journaliserais à l'endroit le plus approprié: la boucle la plus externe du framework.

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }
75
Sake

J'ai toujours pensé que c'est similaire au scénario suivant:

"Un homme se fait tirer dessus!

Il retient son souffle et a assez de force pour prendre un bus.

10 miles plus tard, l'homme descend du bus, marche quelques blocs et meurt. "

Lorsque la police arrive sur le corps, elle n'a aucune idée de ce qui vient de se passer. Ils ont peut-être fini par le faire, mais c'est beaucoup plus difficile.

Mieux vaut:

"Un homme se fait tirer dessus et il meurt instantanément, et le corps se trouve exactement là où le meurtre vient de se produire."

Lorsque la police arrive, toutes les preuves sont en place.

Si un système tombe en panne, mieux vaut échec rapide

Répondre à la question:

  1. Ignorance.
      +
  2. La paresse

MODIFIER:

Bien sûr, la section catch est utile.

Si quelque chose peut être fait avec l'exception, c'est là que cela devrait être fait.

Ce n'est probablement pas une exception pour le code donné, c'est probablement quelque chose qui est attendu (et dans mon analogie, c'est comme une veste pare-balles, et l'homme attendait le tir en premier lieu).

Et oui, la capture pourrait être utilisée pour Lancer des exceptions appropriées à l'abstraction

195
OscarRyz

Habituellement, cela est dû au IDE offrant une "solution rapide" utile qui encapsule le code incriminé dans un bloc try-catch avec cette gestion des exceptions. L'idée est que vous FAITES réellement quelque chose, mais paresseux les développeurs ne le font pas.

C'est une mauvaise forme, sans aucun doute.

33
Kris

C'est un classique argument de l'homme de paille . printStackTrace() est une aide au débogage. Si vous l'avez vu sur un blog ou dans un magazine, c'est parce que l'auteur était plus intéressé à illustrer un point autre que la gestion des exceptions. Si vous l'avez vu dans le code de production, le développeur de ce code était ignorant ou paresseux, rien de plus. Elle ne doit pas être considérée comme un exemple de pratique courante dans le "monde Java".

19
Bill the Lizard
  1. Java vous oblige à gérer explicitement toutes les exceptions. Si une méthode que votre code appelle est déclarée pour lancer FooException et BarException, votre code DOIT gérer (ou lever) ces exceptions. La seule exception à cela est RuntimeException , qui est silencieux comme un ninja.
  2. Beaucoup de programmeurs sont paresseux (moi y compris), et il est très facile d'imprimer simplement la trace de la pile.
19
AgileJon

Je trouve qu'il y a souvent 2 raisons pour lesquelles cela est fait

  1. Le programmeur était paresseux
  2. Le programmeur voulait garder un point d'entrée dans son composant (correctement ou incorrectement)

Je ne pense pas que ce soit un phénomène limité à Java. J'ai également vu un tel codage souvent en C # et VB.Net.

En surface, c'est assez choquant et a l'air terrible. Mais ce n'est vraiment rien de nouveau. Il se produit tout le temps dans les applications C++ qui utilisent des valeurs de retour de code d'erreur par rapport aux exceptions. La différence est que le fait d'ignorer une valeur de retour potentiellement fatale ne ressemble pas vraiment à l'appel d'une fonction qui retourne void.

Foo* pFoo = ...;
pFoo->SomeMethod(); // Void or swallowing errors, who knows?

Ce code semble meilleur mais si SomeMethod () devait dire retourner un HResult, ce ne serait pas sémantiquement différent d'avaler une exception.

12
JaredPar

car Checked Exceptions est une expérience qui a échoué

(peut-être que printStackTrace () est le vrai problème? :)

11
dfa

Je dois dire que je ressens légèrement le ton qui implique que ce type de comportement de gestion des erreurs laxiste est quelque chose de fondamental pour les programmeurs Java. Bien sûr, Java peuvent être paresseux, comme tous les autres programmeurs, et Java est un langage populaire, donc vous verrez probablement beaucoup d'exceptions de code avalant.

En outre, comme cela a été souligné ailleurs, il existe des frustrations compréhensibles avec la déclaration forcée de Java d'exceptions vérifiées, bien que personnellement je n'ai pas de problème avec cela.

Ce qui me pose problème, je suppose, c'est que vous parcourez un tas d'articles et d'extraits de code sur le Web sans prendre la peine de considérer le contexte. La vérité est que lorsque vous écrivez un article technique essayant d'expliquer le fonctionnement d'une API particulière ou comment démarrer avec quelque chose, vous êtes très susceptible de sauter certains aspects du code - la gestion des erreurs qui n'est pas directement lié à ce que vous démontrez est un candidat probable à l'élimination, surtout s'il est peu probable que l'exception se produise dans l'exemple de scénario.

Les gens qui écrivent des articles de cette nature doivent maintenir un rapport signal/bruit raisonnable, et assez honnêtement, je pense, cela signifie qu'ils doivent supposer que vous connaissez quelques notions de base sur le langage dans lequel vous développez; comment gérer correctement les erreurs et bien d'autres choses. Si vous tombez sur un article et que vous constatez un manque de vérification des erreurs, c'est bien; assurez-vous simplement que lorsque vous incorporerez ces idées (mais bien sûr, jamais le code exact lui-même, non?) dans votre code de production, vous traiterez tous ces bits et bobs que l'auteur a sensiblement laissés de côté, d'une manière qui est la plus adapté à ce que vous développez.

J'ai un problème avec les articles d'introduction de très haut niveau qui survolent ces problèmes sans jamais y revenir, mais sachez qu'il n'y a pas de "mentalité" particulière des programmeurs Java Java concernant la gestion des erreurs ; Je connais beaucoup de vos programmeurs C # bien-aimés qui ne se soucient pas non plus de gérer tous leurs problèmes.

6
Rob

Une impression System.out ou e.printStackTrace () - ce qui implique l'utilisation de System.out est généralement un drapeau rouge signifiant que quelqu'un n'a pas pris la peine de faire un travail diligent. À l'exception du bureau Java Applications, la plupart des applications Java sont mieux utilisées en utilisant la journalisation).

Si le mode d'échec d'une méthode est une non-opération, il est tout à fait correct de manger une exception, que vous enregistriez la raison (et l'existence) ou non. Plus généralement, cependant, la clause catch devrait prendre une sorte d'action exceptionnelle.

Il est préférable de renvoyer une exception lorsque vous utilisez la capture pour nettoyer une partie du travail à un niveau où les informations nécessaires sont toujours disponibles ou lorsque vous devez transformer l'exception en un type d'exception plus accessible à l'appelant.

5
Tim H

Comme d'autres l'ont souligné, la raison pour laquelle vous voyez cela est pour l'une des trois raisons suivantes:

  1. Un IDE a généré le bloc try-catch
    • Le code a été copié et collé
    • Le développeur a mis la stacktrace pour déboguer mais n'est jamais revenu pour gérer correctement l'exception

Le dernier point est le moins susceptible de se produire. Je dis cela parce que je pense que personne ne débogue vraiment de cette façon. Parcourir le code avec un débogueur est un moyen beaucoup plus facile de déboguer.

La meilleure description de ce que doit faire dans un bloc catch se trouve dans le chapitre 9 de ( Java efficace par Joshua Bloch .

3
cwash

Il n'est consommé en silence que si le bloc catch est vraiment vide.

En ce qui concerne les articles, ils sont probablement plus intéressants pour prouver un autre point en plus de la façon de traiter les exceptions. Ils veulent juste aller droit au but et avoir le code le plus court possible.

Évidemment, vous avez raison, les exceptions doivent au moins être enregistrées si elles doivent être "ignorées".

3

Je crains que la plupart des programmeurs Java Java ne savent pas quoi faire des exceptions, et les considèrent toujours comme une gêne qui ralentit leur codage du cas "nominal". Bien sûr, ils " re totalement faux, mais il est difficile de les convaincre que IT IS important de traiter correctement les exceptions. Chaque fois que je rencontre un tel programmeur (cela arrive fréquemment) je lui donne deux entrées de lecture:

  • la fameuse Pensée en Java
  • Un court et intéressant article de Barry Ruzek disponible ici: www.Oracle.com/technology/pub/articles/dev2Arch/2006/11/effective-exceptions.html

Soit dit en passant, je suis tout à fait d'accord pour dire qu'il est stupide d'attraper une exception typée pour la renvoyer incorporée dans une RuntimeException:

  • si vous l'attrapez, manipulez-le.
  • sinon, changez la signature de votre méthode pour ajouter des exceptions possibles que vous ne pourriez/ne pourriez pas gérer, afin que votre appelant puisse le faire lui-même.
2

En C #, toutes les exceptions sont des exceptions d'exécution, mais en Java vous avez des exceptions d'exécution et des exceptions vérifiées, que vous devez soit intercepter, soit déclarer dans vos méthodes. Si vous appelez une méthode qui a un " throws "à la fin, vous devez soit intercepter les exceptions qui y sont mentionnées, soit votre méthode doit également déclarer ces exceptions.

Les articles Java impriment généralement la trace de la pile ou comportent un commentaire, car la gestion des exceptions n'est pas pertinente pour le sujet de l'article. Dans les projets cependant, quelque chose devrait être fait à ce sujet, selon le type d'exception.

2
Chochos

La combinaison d'exceptions et d'interfaces vérifiées conduit à la situation dans laquelle le code doit gérer des exections qui ne sont jamais levées. (Il en va de même pour l'héritage normal, mais c'est plus courant et plus facile à expliquer avec les interfaces)

Motif: la mise en œuvre d'une interface ne peut pas générer d'exceptions (vérifiées) autres que celles définies dans la spécification d'interface. Pour cette raison, les créateurs d'une interface, ne sachant pas quelles méthodes d'une classe implémentant l'interface pourraient réellement avoir besoin de lever une exception, pourraient spécifier que toutes les méthodes peuvent lever au moins un type d'exception. Exemple: JDBC, où tout et sa grand-mère sont déclarés pour lancer SQLException.

Mais en réalité, de nombreuses méthodes d'implémentations réelles ne peuvent tout simplement pas échouer, donc en aucun cas, elles ne lèvent une exception. Le code appelant ces méthodes doit toujours "gérer" l'exception, et la manière la plus simple consiste à faire avaler l'exception. Personne ne veut encombrer son code avec une gestion des erreurs apparemment inutile qui n'est jamais exécutée.

2

Vous devriez voir cela très souvent si le programmeur fait bien son travail. Ignorer l'exception est une mauvaise, mauvaise pratique! Mais il y a quelques raisons pour lesquelles certains pourraient le faire et les solutions les plus appropriées:

  • "Cela n'arrivera pas!" Bien sûr, parfois vous "savez" que cette exception ne se produira pas, mais il est encore plus approprié de renvoyer une exception d'exécution avec l'exception survenue comme "cause" au lieu de simplement l'ignorer. Je parie que cela se produira dans le futur. ;-)

  • Code de prototypage Si vous tapez simplement vos données pour voir si cela fonctionne, vous pouvez ignorer toutes les exceptions qui pourraient survenir. C'est le seul cas où je fais des prises paresseuses (Throwable). Mais si le code se révèle utile, j'inclus une gestion des exceptions appropriée.

  • "Je ne sais pas quoi faire!" J'ai vu beaucoup de code, en particulier le code de bibliothèque, qui avale les exceptions qui se produisent, car dans cette couche de l'application, aucune manipulation appropriée ne peut être effectuée. NE FAITES PAS CELA! Renvoyez simplement l'exception (soit en ajoutant une clause throws dans la signature de la méthode, soit en enveloppant l'exception dans une bibliothèque spécifique).

2
Malax

Vous devez toujours le transmettre ou le manipuler convenablement dans un contexte réel. De nombreux articles et tutoriels simplifieront leur code pour faire passer les points les plus importants et l'une des choses les plus simples à simplifier est la gestion des erreurs (sauf si vous écrivez un article sur la gestion des erreurs, c'est :)). Comme Java vérifiera la gestion des exceptions, puis mettre un simple bloc catch silencieux (ou instruction de journalisation) est la méthode la plus simple pour fournir un exemple de travail.

Si vous trouvez cela dans autre chose qu'un exemple de code, n'hésitez pas à transférer le code sur TDWTF, bien qu'ils puissent en avoir trop d'exemples maintenant :)

2
workmad3

Je ne suis pas d'accord pour dire que la levée d'une exception vérifiée est une meilleure idée. La capture signifie la manipulation; si vous devez retourner, vous ne devriez pas attraper. J'ajouterais la clause throws à la signature de la méthode dans ce cas.

Je dirais que l'encapsulation d'une exception vérifiée dans une exception non vérifiée (par exemple, la façon dont Spring encapsule l'exception SQLException vérifiée dans une instance de sa hiérarchie non vérifiée) est acceptable.

La journalisation peut être considérée comme une manipulation. Si l'exemple a été modifié pour enregistrer la trace de la pile à l'aide de log4j au lieu d'écrire sur la console, est-ce que cela le rendrait acceptable? Pas beaucoup de changement, OMI.

Le vrai problème est ce qui est considéré comme exceptionnel et une procédure de récupération acceptable. Si vous ne pouvez pas récupérer de l'exception, le mieux que vous puissiez faire est de signaler l'échec.

1
duffymo

Veuillez ne jamais, jamais, jamais envelopper une exception cochée dans une exception non cochée.

Si vous vous retrouvez face à des exceptions que vous ne pensez pas devoir, alors mon conseil est que vous travaillez probablement au mauvais niveau d'abstraction.

Je vais élaborer: les exceptions vérifiées et non vérifiées sont deux bêtes très différentes. Les exceptions vérifiées sont similaires à l'ancienne méthode de retour des codes d'erreur ... oui, elles sont un peu plus pénibles à gérer que les codes d'erreur, mais elles ont aussi des avantages. Les exceptions non vérifiées sont des erreurs de programmation et des pannes critiques du système ... des exceptions exceptionnelles en d'autres termes. En essayant d'expliquer ce qu'est une exception, beaucoup de gens se retrouvent dans un désordre complet parce qu'ils ne reconnaissent pas la différence dans ces deux cas très différents.

1
CurtainDog

Le vrai point des exceptions est de simplifier la gestion des erreurs et de la séparer de la détection des erreurs. Ceci est contraire à la représentation des erreurs par des codes d'erreur, où le code de gestion des erreurs est dispersé partout et chaque appel qui peut échouer doit être vérifié pour le code retour.

Si l'exception représente une erreur (ce qui est le cas dans la plupart des cas), la manière la plus raisonnable de la gérer est généralement de renflouer et de laisser la manipulation à une couche supérieure. Renvoyer une exception différente doit être envisagée si une sémantique significative y est ajoutée, c'est-à-dire que cette erreur est une défaillance système inhabituelle/un problème temporaire (réseau)/il s'agit d'une erreur côté client ou serveur, etc.

De toutes les stratégies de gestion des erreurs, la plus ignorante consiste à cacher ou simplement à imprimer un message d'erreur et à continuer car rien ne s'est produit.


Les gens de Sun voulaient que le code soit plus explicite et obligeait les programmeurs à écrire quelles exceptions pouvaient être levées par quelle méthode. Cela semblait être la bonne décision - tout le monde saura à quoi s'attendre en retour de tout appel de méthode étant donné son prototype (il peut retourner une valeur de ce type ou lancer une instance de l'une des classes spécifiées (ou de sa sous-classe)).

Mais comme il s'est avéré avec beaucoup de programmeurs ignorants Java, ils traitent maintenant la gestion des exceptions comme s'il s'agissait d'un bogue/"fonctionnalité" de langage qui nécessitait une solution de contournement et écrivait du code de la pire ou presque la pire façon possible :

  • L'erreur est traitée immédiatement dans un contexte qui ne permet pas de décider quoi en faire.
  • Il est affiché ou ignoré en silence et l'informatique continue même lorsque le code supplémentaire n'a aucune chance de fonctionner correctement.
  • L'appelant de méthode ne peut pas différencier s'il s'est terminé avec succès ou non.

Comment écrire la "bonne façon" que?

  • Indiquez toutes les classes de base d'exceptions qui peuvent être levées dans l'en-tête de la méthode. AFAICR Eclipse peut le faire automatiquement.
  • Donnez un sens à la liste des lancers dans le prototype de méthode. Les longues listes sont inutiles et "lever l'exception" est paresseux (mais utile lorsque vous ne vous souciez pas beaucoup des exceptions).
  • Lors de l'écriture de la "mauvaise façon", une simple "exception exception" est bien meilleure et prend moins d'octets que "essayez {...} catch (Exception e) {e.printStackTrace ();}".
  • Renvoyez l'exception chaînée si nécessaire.
1
lispmachine

Comme souligné, appeler printStackTrace () n'est pas vraiment une manipulation silencieuse.

La raison de ce type de "déglutition" de l'exception est que, si vous continuez à transmettre l'exception dans la chaîne, vous devez toujours gérer l'exception quelque part ou laisser l'application se bloquer. Ainsi, le gérer au niveau où il se produit avec un vidage d'informations n'est pas pire que le gérer au niveau supérieur avec un vidage d'informations.

1
Yes - that Jake.

Sa pratique paresseuse - rien de moins que ça.

C'est généralement fait lorsque vous ne vous souciez vraiment pas de l'exception - plutôt que d'augmenter votre travail avec les doigts.

1
Matt

Parce qu'ils n'ont pas encore appris cette astuce:

class ExceptionUtils {
    public static RuntimeException cloak(Throwable t) {
        return ExceptionUtils.<RuntimeException>castAndRethrow(t);
    }

    @SuppressWarnings("unchecked")
    private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
        throw (X) t;
    }
}

class Main {
    public static void main(String[] args) { // Note no "throws" declaration
        try {
            // Do stuff that can throw IOException
        } catch (IOException ex) {
            // Pretend to throw RuntimeException, but really rethrowing the IOException
            throw ExceptionUtils.cloak(ex);
        }
    }
}
1
finnw

Si vous avez une exception vérifiée et que vous ne voulez pas la gérer dans une méthode, vous devez simplement demander à la méthode de lever l'exception. N'attrapez et ne gérez les exceptions que si vous comptez en faire quelque chose d'utile. La journalisation n'est pas très utile dans mon livre car les utilisateurs ont rarement le temps de lire les journaux à la recherche d'exceptions ou de savoir quoi faire si une exception est levée.

Bien que l'encapsulation de l'exception soit une option, je ne vous suggérerais pas de le faire à moins que; vous lancez une exception différente pour correspondre à une interface existante ou il n'y a vraiment aucun moyen de lever une telle exception.

BTW: Si vous voulez relancer une exception cochée, vous pouvez le faire avec

try {
   // do something
} catch (Throwable e) {
   // do something with the exception
   Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
}

Remarque: si vous faites cela, vous devez vous assurer que la déclaration throws pour la méthode est correcte car le compilateur ne peut pas le faire pour vous dans cette situation.

0
Peter Lawrey

Il peut y avoir de nombreuses raisons pour lesquelles on utilise catch exception. Dans de nombreux cas, c'est une mauvaise idée car vous interceptez également des RuntimeExceptions - et vous ne savez pas dans quel état les objets sous-jacents seront après cela? C'est toujours la chose difficile avec des conditions inattendues: pouvez-vous croire que le reste du code n'échouera pas par la suite.

Votre exemple imprime la trace de pile afin que vous sachiez au moins quelle pourrait être la cause première. Dans les grands projets logiciels, il est préférable de consigner ces éléments. Et espérons que le composant log ne lève pas d'exceptions non plus, vous pourriez vous retrouver dans une boucle infinie (ce qui tuera probablement votre JVM).

0
David Nouls

Si vous souhaitez que votre exception soit gérée en dehors de la portée de la méthode actuelle, vous n'avez pas besoin de l'attraper réellement, à la place, vous ajoutez l'instruction 'throws' à la signature de la méthode.

Les instructions try/catch que vous avez vues se trouvent uniquement dans le code où le programmeur a explicitement décidé de gérer l'exception en place et donc de ne pas la lancer plus loin.

0
quosoo

Je pense que les développeurs essaient également de considérer l'importance de "faire la bonne chose" dans un contexte particulier. Souvent, le fait de jeter du code ou de propager l'exception vers le haut n'achèterait rien car l'exception est fatale, vous pourriez aussi bien gagner du temps et des efforts en "faisant la mauvaise chose".

0
kmorris511

Par expérience, la déglutition d'une exception est nuisible surtout lorsqu'elle n'est pas imprimée. Cela aide à attirer l'attention si vous vous plantez, et je le ferai parfois délibérément, mais simplement imprimer l'exception et continuer vous permet de trouver le problème et de le résoudre, mais cela n'affecte généralement pas les autres travaillant sur la même base de code. .

Je suis en fait dans Fail-Fast/Fail-HARD, mais au moins l'imprimer. Si vous "mangez" réellement une exception (qui est vraiment de ne rien faire: {}) Cela coûtera à quelqu'un DAYS pour la trouver.

Le problème est que Java vous oblige à attraper beaucoup de choses dont le développeur sait qu'elles ne seront pas lancées, ou s'en fout qu'elles le soient. La plus courante est Thread.sleep () Je me rends compte qu'il y a des trous qui pourraient permettre des problèmes de filetage ici, mais généralement vous savez que vous ne l'interrompez pas.

0
Bill K

Vous avaleriez généralement une exception lorsque vous ne pourrez pas vous en remettre, mais ce n'est pas critique. Un bon exemple est l'IOExcetion qui peut être lancé lors de la fermeture d'une connexion à une base de données. Voulez-vous vraiment planter si cela se produit? C'est pourquoi les DBUtils de Jakarta ont des méthodes closeSilently.

Maintenant, pour les exceptions vérifiées que vous ne pouvez pas récupérer mais qui sont critiques (généralement en raison d'erreurs de programmation), ne les avalez pas. Je pense que les exceptions doivent être enregistrées le plus près possible de la source du problème, donc je ne recommanderais pas de supprimer l'appel printStackTrace (). Vous souhaiterez les transformer en RuntimeException dans le seul but de ne pas avoir à déclarer ces exceptions dans votre méthode commerciale. Vraiment, cela n'a pas de sens d'avoir des méthodes métier de haut niveau telles que createClientAccount () lève ProgrammingErrorException (lire SQLException), il n'y a rien que vous puissiez faire si vous avez des fautes de frappe dans votre sql ou accéder à de mauvais index.

0
svachon