Est-ce une mauvaise pratique d'attraper Throwable?


Réponses:


104

Vous devez être aussi précis que possible. Sinon, des bugs imprévus pourraient s'éloigner de cette façon.

En outre, les Throwablecouvertures Erroraussi et ce n'est généralement pas un point de retour . Vous ne voulez pas attraper / gérer cela, vous voulez que votre programme meure immédiatement afin que vous puissiez le réparer correctement.


38
Il y a des situations où il est approprié d'attraper Error et de continuer. Ex: Dans un servlet, si vous rencontrez une OutOfMemoryError parce qu'une requête spécifique arrive à consommer toute la mémoire, vous pouvez essayer de continuer car les objets seront GC après le traitement de la requête. Il en va de même pour une erreur d'assertion. Vous ne fermez pas une application car une erreur s'est produite dans une demande.
gawi

7
Comment savez-vous ce qui a été alloué et ce qui ne l'était pas avant l'OOME? Tous les paris sont ouverts une fois que vous obtenez cela, même à l'intérieur d'un conteneur J2EE comme Tomcat ou JBoss.
bmauter

10
Nous avons eu NoSuchMethodError et, heureusement, nous n'avons pas éliminé tous nos clients en arrêtant le serveur comme cela s'est produit deux semaines après le déploiement. c'est à dire. Nous avons toujours un fourre-tout attrapant Throwable et faisons de notre mieux pour gérer et envoyer l'erreur au client. Après tout, il existe de nombreux types d'erreur qui sont récupérables en ce sens qu'ils ne peuvent affecter qu'un seul client sur 1000.
Dean Hiller

10
" vous voulez que votre programme meure immédiatement pour pouvoir le réparer correctement " => si votre programme meurt, comment savez-vous ce qui s'est passé? Attraper Throwable / Error pour enregistrer le problème est une chose raisonnable à faire ...
Assylias

3
@assylias Une application autonome lèvera une erreur fatale à stderr.
Philip Whitehouse

36

C'est une mauvaise idée. En fait, même attraper Exceptionest généralement une mauvaise idée. Prenons un exemple:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

Maintenant, disons que getUserInput () se bloque pendant un certain temps et qu'un autre thread arrête votre thread de la pire manière possible (il appelle thread.stop ()). Votre bloc catch attrapera une ThreadDeatherreur. C'est super mauvais. Le comportement de votre code après avoir détecté cette exception est en grande partie indéfini.

Un problème similaire se produit lors de la capture d'Exception. Peut-être a getUserInput()échoué en raison d'une InterruptException, ou d'une exception d'autorisation refusée lors de la tentative de journalisation des résultats, ou de toutes sortes d'autres échecs. Vous n'avez aucune idée de ce qui n'a pas fonctionné, car à cause de cela, vous ne savez pas non plus comment résoudre le problème.

Vous avez trois meilleures options:

1 - Attrapez exactement la ou les exceptions que vous savez gérer:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 - Relancez toute exception que vous rencontrez et ne savez pas comment gérer:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 - Utilisez un bloc finally pour ne pas avoir à vous rappeler de relancer:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

4
+1 pour avoir déclaré que même attraper une exception n'est pas bon. Au moins, il y a une ThreadInterruptedException qui nécessite une attention particulière (en bref - après l'avoir attrapée, vous devez
redéfinir le

Je sais que c'est juste pour illustrer vos mots, mais je pense que vous pouvez vérifier avec une expression régulière si votre entrée utilisateur est alphanumérique ou de quel format vous avez besoin et n'utilisez pas try catch partout à chaque fois.
amdev

Comment suis-je supposé savoir s'il y a une erreur / rejetable si je ne l'attrape pas? Je n'ai rien vu dans les journaux. Application Java EE. Je viens de passer des jours coincés sans savoir quel était le problème jusqu'à ce que j'ajoute cette capture.
Philip Rego

2
Je considère l'option n ° 2 comme une mauvaise pratique. Imaginez 10 appels enchaînés avec log & rethrow. Si vous regardez le fichier journal, vous ne serez pas content. L'exception est enregistrée 10 fois, ce qui rend les journaux très difficiles à lire. IMHO beaucoup mieux est de faire throw new Exception("Some additional info, eg. userId " + userId, e);. Cela sera enregistré dans une belle exception avec 10 causes.
Petr Újezdský

21

Sachez également que lorsque vous attrapez Throwable, vous pouvez également attraper InterruptedExceptionce qui nécessite un traitement spécial. Pour plus de détails, consultez Traitement de l'exception InterruptedException .

Si vous souhaitez uniquement intercepter les exceptions non contrôlées, vous pouvez également envisager ce modèle

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

De cette façon, lorsque vous modifiez votre code et ajoutez un appel de méthode qui peut lever une exception vérifiée, le compilateur vous le rappellera et vous pourrez ensuite décider quoi faire dans ce cas.


14

directement à partir du javadoc de la classe Error (qui recommande de ne pas les attraper):

 * An <code>Error</code> is a subclass of <code>Throwable</code> 
 * that indicates serious problems that a reasonable application 
 * should not try to catch. Most such errors are abnormal conditions. 
 * The <code>ThreadDeath</code> error, though a "normal" condition,
 * is also a subclass of <code>Error</code> because most applications
 * should not try to catch it. 

 * A method is not required to declare in its <code>throws</code> 
 * clause any subclasses of <code>Error</code> that might be thrown 
 * during the execution of the method but not caught, since these 
 * errors are abnormal conditions that should never occur. 
 *
 * @author  Frank Yellin
 * @version %I%, %G%
 * @see     java.lang.ThreadDeath
 * @since   JDK1.0

13

Ce n'est pas une mauvaise pratique si vous ne pouvez absolument pas avoir une bulle d'exception hors d'une méthode.

C'est une mauvaise pratique si vous ne pouvez vraiment pas gérer l'exception. Mieux vaut ajouter des "jets" à la signature de la méthode que simplement attraper et relancer ou, pire, l'envelopper dans une RuntimeException et la relancer.


10
Tout à fait d'accord - il existe des cas absolument légitimes pour gérer toutes les Throwableinstances - par exemple pour la journalisation des exceptions personnalisée.
Yuriy Nakonechnyy du

9

Catching Throwable est parfois nécessaire si vous utilisez des bibliothèques qui lancent des erreurs avec trop d'enthousiasme, sinon votre bibliothèque peut tuer votre application.

Cependant, il serait préférable dans ces circonstances de ne spécifier que les erreurs spécifiques générées par la bibliothèque, plutôt que tous les Throwables.


12
Ou utiliser une meilleure bibliothèque écrite?
Raedwald le

6
En effet, si vous avez le choix ;-)
DNA

c'est le plus gros problème pour attraper les objets jetables et les relancer. cela rend vraiment une interface impossible pour toutes les méthodes en amont de la pile. Ils doivent soit faire face à un jetable, soit avoir une signature jetable inutilisable que d'autres devront gérer.
Andrew Norman

6

Throwable est la classe de base pour toutes les classes qui peuvent être levées (pas seulement les exceptions). Vous ne pouvez pas faire grand-chose si vous attrapez une OutOfMemoryError ou KernelError (voir Quand attraper java.lang.Error? )

attraper des exceptions devrait suffire.


5

cela dépend de votre logique ou pour être plus spécifique à vos options / possibilités. S'il y a une exception spécifique sur laquelle vous pouvez éventuellement réagir de manière significative, vous pouvez la détecter en premier et le faire.

S'il n'y en a pas et que vous êtes sûr que vous ferez la même chose pour toutes les exceptions et erreurs (par exemple, quitter avec un message d'erreur), alors ce n'est pas un problème pour attraper le jetable.

Habituellement, le premier cas tient et vous n'attraperez pas le jetable. Mais il existe encore de nombreux cas où l'attraper fonctionne bien.


4

Bien que cela soit décrit comme une très mauvaise pratique, vous pouvez parfois trouver de rares cas où cela est non seulement utile mais également obligatoire. Voici deux exemples.

Dans une application Web où vous devez montrer une page d'erreur complète à l'utilisateur. Ce code s'assure que cela se produit car il s'agit d'un gros try/catchautour de tous vos gestionnaires de requêtes (servlets, actions struts ou tout contrôleur ....)

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

Comme autre exemple, considérons que vous avez une classe de service qui sert les activités de transfert de fonds. Cette méthode renvoie un TransferReceiptsi le transfert est effectué ou NULLs'il n'a pas pu.

String FoundtransferService.doTransfer( fundtransferVO);

Maintenant, vous obtenez une image Listdes transferts de fonds de l'utilisateur et vous devez utiliser le service ci-dessus pour les faire tous.

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

Mais que se passera-t-il si une exception se produit? Vous ne devez pas vous arrêter, car un transfert peut avoir été un succès et un autre non, vous devez continuer à travers tous les utilisateurs Listet afficher le résultat à chaque transfert. Donc, vous vous retrouvez avec ce code.

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

Vous pouvez parcourir de nombreux projets open source pour voir que le throwableest vraiment mis en cache et géré. Par exemple, voici une recherche sur tomcat, struts2et primefaces:

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable


1
Vu le code dans ces liens. Throwable n'est pas seulement celui qui est attrapé! Il y a aussi d'autres exceptions capturées avant Throwable.
developer1011

@ developer101 bien sûr, mais ils attrapent throwable, c'est sur quoi porte cette question
Alireza Fattahi

4

La question est un peu vague; demandez-vous "est-il OK pour attraper Throwable", ou "est-il OK pour attraper Throwableet ne rien faire"? Beaucoup de gens ici ont répondu à cette dernière question, mais c'est un problème secondaire; 99% du temps , vous ne devriez pas « consommer » ou rejeter l'exception, si vous attrapez Throwableou IOExceptionou autre.

Si vous propagez l'exception, la réponse (comme la réponse à tant de questions) est "ça dépend". Cela dépend de ce que vous faites à l'exception de la raison pour laquelle vous l'attrapez.

Un bon exemple des raisons pour lesquelles vous voudriez attraper Throwableest de fournir une sorte de nettoyage en cas d'erreur. Par exemple dans JDBC, si une erreur se produit pendant une transaction, vous voudrez annuler la transaction:

try {
  
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

Notez que l'exception n'est pas rejetée, mais propagée.

Mais en Throwablerègle générale, attraper parce que vous n'avez pas de raison et que vous êtes trop paresseux pour voir quelles exceptions spécifiques sont lancées est une mauvaise forme et une mauvaise idée.


1

De manière générale, vous voulez éviter d'attraper des Errors mais je peux penser à (au moins) deux cas spécifiques où il convient de le faire:

  • Vous souhaitez arrêter l'application en réponse à des erreurs, en particulier AssertionError ce qui est par ailleurs inoffensif.
  • Implémentez-vous un mécanisme de regroupement de threads similaire à ExecutorService.submit () qui vous oblige à renvoyer les exceptions à l'utilisateur afin qu'il puisse le gérer.

0

Si nous utilisons throwable , cela couvre également Error et c'est tout.

Exemple.

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

Production:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

0

Throwable est la superclasse de toutes les erreurs et excétions. Si vous utilisez Throwable dans une clause catch, il détectera non seulement toutes les exceptions, mais également toutes les erreurs. Des erreurs sont émises par la JVM pour indiquer des problèmes graves qui ne sont pas destinés à être traités par une application. Des exemples typiques pour cela sont OutOfMemoryError ou StackOverflowError. Les deux sont causés par des situations qui sont hors du contrôle de l'application et ne peuvent pas être gérées. Donc, vous ne devriez pas attraper Throwables à moins que vous ne soyez convaincu que ce ne sera qu'une exception résidant dans Throwable.


-1

Bien qu'il soit généralement une mauvaise pratique d'attraper Throwable (comme le montrent les nombreuses réponses à cette question), les scénarios où la capture Throwableest utile sont assez courants. Permettez-moi de vous expliquer un cas que j'utilise dans mon travail, avec un exemple simplifié.

Considérez une méthode qui effectue l'ajout de deux nombres, et après l'ajout réussi, elle envoie une alerte par e-mail à certaines personnes. Supposons que le nombre retourné est important et utilisé par la méthode appelante.

public Integer addNumbers(Integer a, Integer b) {
    Integer c = a + b;          //This will throw a NullPointerException if either 
                                //a or b are set to a null value by the
                                //calling method
    successfulAdditionAlert(c);
    return c;
}

private void successfulAdditionAlert(Integer c) {
    try {
        //Code here to read configurations and send email alerts.
    } catch (Throwable e) {
        //Code to log any exception that occurs during email dispatch
    }
}

Le code pour envoyer des alertes par e-mail lit un grand nombre de configurations système et, par conséquent, il peut y avoir une variété d'exceptions lancées à partir de ce bloc de code. Mais nous ne voulons pas qu'une exception rencontrée lors de l'envoi d'alertes se propage à la méthode de l'appelant, car cette méthode est simplement concernée par la somme des deux valeurs Integer qu'elle fournit. Par conséquent, le code pour envoyer les alertes par e-mail est placé dans un try-catchbloc, où Throwableest intercepté et toutes les exceptions sont simplement enregistrées, ce qui permet au reste du flux de continuer.


J'essaierais d'éviter cela en ayant un fil dédié au travail (avec une file d'attente) d'envoi d'e-mails.
boumbh

Je suggérerais que ce code est toujours mauvais. Catch Exceptions par tous les moyens, mais pas Throwable.
andrewf
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.