Nous avons récemment rencontré un problème avec une application serveur Java où l’application émettait des erreurs qui n’étaient pas interceptées car Error est une sous-classe distincte de Throwable et que nous ne capturions que des exceptions.
Nous avons résolu le problème immédiat en interceptant Throwables plutôt que des exceptions, mais cela m’a fait réfléchir à la raison pour laquelle vous voudriez jamais intercepter des exceptions, plutôt que des Throwables, car les erreurs vous manqueraient.
Alors, pourquoi voudriez-vous attraper des exceptions, quand vous pouvez attraper des Throwables ?
Tout dépend un peu de ce que vous allez faire avec une erreur une fois que vous l’avez détecté. En général, les erreurs de capture ne devraient probablement pas être considérées comme faisant partie de votre stream d’exceptions “normales”. Si vous en attrapez un, vous ne devriez pas penser à “continuer comme si rien ne s’était passé”, car la JVM (et diverses bibliothèques) utiliserait des erreurs pour signaler que “quelque chose de vraiment grave est arrivé et que nous devons fermer le plus vite possible “. En général, il est préférable de les écouter quand ils vous disent que la fin est proche.
Un autre problème est que la récupérabilité d’une erreur peut dépendre de la machine virtuelle particulière, ce que vous pouvez ou non contrôler.
Cela dit, il y a quelques cas isolés où il est sécuritaire et / ou souhaitable de détecter des erreurs, ou au moins certaines sous-classes:
Donc, le résultat est le suivant: si vous attrapez Throwable / Error plutôt que Exception, cela devrait être un cas bien défini où vous savez que vous “faites quelque chose de spécial” .
Edit: C’est peut-être évident, mais j’ai oublié de dire qu’en pratique, la JVM pourrait ne pas invoquer votre clause catch sur une erreur. J’ai certainement vu Hotspot briller sur les tentatives d’attraper certains OutOfMemoryErrors et NoClassDefFoundError.
A partir de la documentation de l’API Java:
La classe
Exception
et ses sous-classes sont une forme deThrowable
qui indique les conditions qu’une application raisonnable peut vouloir intercepter.Une
Error
est une sous-classe deThrowable
qui indique des problèmes sérieux qu’une application raisonnable ne doit pas tenter d’attraper.
Les erreurs sont généralement de bas niveau (par exemple, déclenchées par la machine virtuelle) et ne doivent pas être interceptées par l’application car une suite raisonnable pourrait ne pas être possible.
Habituellement, les erreurs sont des problèmes dont vous ne pouvez pas récupérer, comme OutOfMemoryError . Il n’y a rien à faire en les attrapant, vous devriez donc les laisser s’échapper et faire tomber la machine virtuelle.
Un grand nombre des autres réponses examinent les choses de manière trop étroite.
Comme ils disent, si vous écrivez le code d’application, vous ne devriez pas attraper Throwable. Vous ne pouvez rien y faire, alors mieux vaut laisser le système environnant (JVM ou framework) gérer ces problèmes.
Toutefois, si vous écrivez “code système”, comme un framework ou un autre code de bas niveau, vous pouvez très bien vouloir attraper Throwable. La raison est d’ essayer de signaler l’exception, peut-être dans un fichier journal. Dans certains cas, votre journalisation échouera, mais dans la plupart des cas, cela réussira et vous aurez les informations nécessaires pour résoudre le problème. Une fois que vous avez effectué votre tentative de journalisation, vous devez alors renvoyer, tuer le thread en cours ou quitter la JVM entière.
Je vais suivre un itinéraire légèrement différent des autres.
Il y a beaucoup de cas où vous voudriez attraper Throwable (principalement pour enregistrer / signaler que quelque chose de mal est arrivé).
Cependant , vous devez faire attention et relancer tout ce que vous ne pouvez pas traiter.
Cela est particulièrement vrai pour ThreadDeath.
Si vous attrapez jamais Throwable, assurez-vous de faire ce qui suit:
try { ... } catch (SomeExceptionYouCanDoSomethingWith e) { // handle it } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // log & rethrow }
Ne jamais attraper Throwable
ou Error
et vous ne devriez pas non plus attraper une Exception
générique. Error
sont généralement des choses dont la plupart des programmes raisonnables ne peuvent pas récupérer. Si vous savez ce qui se passe, vous pourriez être en mesure de récupérer d’une erreur spécifique, mais dans ce cas, vous ne devriez intercepter que cette erreur particulière et pas toutes les erreurs en général.
Une bonne raison pour ne pas attraper Error
est due à ThreadDeath
. ThreadDeath
est une occurrence assez normale qui peut théoriquement être lancée de n’importe où (d’autres processus, comme la JVM elle-même, peuvent la générer), et le but est de détruire votre thread. ThreadDeath
est explicitement une Error
plutôt qu’une Exception
car trop de personnes interceptent toutes les Exception
. Si vous ThreadDeath
attraper ThreadDeath
, vous devez le relancer afin que votre thread meurt réellement.
Si vous contrôlez la source, celle-ci devrait probablement être restructurée pour générer une Exception
plutôt qu’une Error
. Si vous ne le faites pas, vous devriez probablement appeler le fournisseur et vous plaindre. Error
s doivent être réservées uniquement aux éléments terminaux sans moyen de les récupérer.
Il y a au moins un cas où je pense que vous devrez peut-être intercepter une exception jetable ou une exception générique – si vous exécutez un thread séparé pour effectuer une tâche, vous voudrez peut-être savoir si la méthode “run” du thread en a capturé exception ou non. Dans ce cas, vous ferez probablement quelque chose comme ceci:
public void run() { try { ... } catch(Throwable t) { threadCompletionError = t; } }
Je ne suis vraiment pas sûr que ce soit la meilleure approche, mais ça marche. Et j’avais une erreur “ClassNotFound” soulevée par la JVM, et c’est une erreur et non une exception. Si je laisse tomber l’exception, je ne sais pas comment l’attraper dans le thread appelant (il y a probablement une méthode mais je ne le sais pas encore).
Comme pour la méthode ThreadDeath, n’appelez pas la méthode “Thread.stop ()”. Appelez Thread.interrupt et demandez à votre thread de vérifier s’il a été interrompu par quelqu’un.
Normalement, lors de la programmation, vous ne devez capturer qu’une exception spécifique (telle que IOException
). Dans beaucoup de programmes, vous pouvez voir un très haut niveau
try { ... } catch(Exception e) { ... }
Cela intercepte toutes les erreurs qui pourraient être récupérées et toutes celles qui indiquent un bogue dans votre code, par exemple InvalidArgumentException
, NullPointerException
. Vous pouvez ensuite envoyer automatiquement un courrier électronique, afficher une boîte de message ou tout ce que vous voulez, car le JavaVM lui-même fonctionne toujours correctement.
Tout ce qui provient de Error
est quelque chose de très mauvais, on ne peut rien faire contre. La question est, s’il est logique d’attraper une OutOfMemoryError
ou une VirtualMachineError
. (C’est une erreur dans le JavaVM lui-même, vous ne pouvez probablement même pas afficher un message ou envoyer un e-mail)
Vous ne devriez probablement pas une classe dérivée d’ Error
, vous devriez dériver d’ Exception
ou RuntimeException
.
Je sais que cela peut être contre-intuitif, mais ce n’est pas parce que vous pouvez attraper toutes sortes d’exceptions et Throwables et Erreurs que vous devriez le faire.
Une capture trop agressive de java.lang.Exception peut conduire à des bogues graves dans les applications – car les exceptions inattendues ne font jamais surface, ne sont jamais détectées lors du développement / des tests, etc.
Meilleure pratique: attraper seulement
En général, il serait raisonnable d’essayer d’attraper les erreurs, ne serait-ce que pour que cela puisse être correctement signalé.
Cependant, je pense qu’il y a des cas où il serait approprié d’attraper une erreur et de ne pas la signaler. Je fais référence à UnsatisfiedLinkError. Dans JAI, la bibliothèque utilise des bibliothèques natives pour implémenter la plupart des opérateurs pour des raisons de performances. Cependant, si la bibliothèque ne parvient pas à se charger (n’existe pas, format incorrect, plateforme non prise en charge), la bibliothèque fonctionnera .
Cet article ne rendra pas heureux les “exceptions vérifiées”. Cependant, je me base sur la manière dont les exceptions Java sont conçues pour être utilisées par les personnes qui ont créé le langage.
Tableau de référence rapide:
La raison pour laquelle vous ne devez pas intercepter Exception est qu’il intercepte toutes les sous-classes, y compris RuntimeException.
La raison pour laquelle vous ne devez pas intercepter Throwable est qu’il intercepte toutes les sous-classes, y compris Error et Exception.
Il y a des exceptions (sans jeu de mots) aux “règles” ci-dessus:
Pour le second, il suffit généralement de placer le code principal, le code de gestion des événements et les threads avec le composant catch dans Throwable, puis de vérifier le type réel de l’exception et de le traiter comme il convient.
Un peu hors sujet, mais vous voudrez peut-être aussi regarder ce très bon article sur les exceptions.
Pourquoi ne pas les attraper tous? Ensuite, connectez-les, au moins vous savez que vous avez une erreur. Donc mieux vaut attraper Throwable / s que Exception / s seulement.
Il n’y a pas de raison d’attraper une erreur .
Les erreurs sont utilisées pour indiquer que quelque chose a vraiment mal tourné dans votre application et qu’il doit être redémarré.
Par exemple, une erreur commune est
java.lang.OutOfMemoryError
Il n’y a rien que vous puissiez faire quand cela se produit. Est déjà trop tard, la JVM a épuisé toutes ses options pour obtenir plus de mémoire mais c’est impossible.
Voir cette autre réponse pour mieux comprendre les trois types d’exceptions .