È una cattiva pratica catturare Throwable?


Risposte:


104

Devi essere il più specifico possibile. In caso contrario, bug imprevisti potrebbero insinuarsi in questo modo.

Inoltre, anche le Throwablecopertine Errore di solito non è un punto di ritorno . Non vuoi prenderlo / gestirlo, vuoi che il tuo programma muoia immediatamente in modo da poterlo riparare correttamente.


38
Ci sono situazioni in cui è appropriato catturare l'errore e continuare. Es: in un servlet, se inconterai un OutOfMemoryError perché una richiesta specifica mangia tutta la memoria, puoi provare a continuare poiché gli oggetti saranno GC dopo che la richiesta è stata gestita. Lo stesso vale per un errore di asserzione. Non chiudi un'applicazione perché qualcosa è andato storto in una richiesta.
gawi

7
Come fai a sapere cosa è stato assegnato e cosa non lo era prima dell'OOME? Tutte le scommesse vengono annullate una volta ottenuto, anche all'interno di un contenitore J2EE come Tomcat o JBoss.
bmauter

10
Abbiamo avuto NoSuchMethodError e per fortuna non abbiamo eliminato tutti i nostri clienti spegnendo il server come è successo due settimane dopo la distribuzione. vale a dire. Abbiamo sempre il compito di catturare Throwable e fare del nostro meglio per gestire e inviare errori al cliente. Dopo tutto, ci sono molti tipi di errore che possono essere recuperati in quanto possono interessare solo 1 cliente su 1000.
Dean Hiller

10
" vuoi che il tuo programma muoia immediatamente in modo da poterlo riparare correttamente " => se il tuo programma muore, come fai a sapere cosa è successo? Catturare Throwable / Error per registrare il problema è una cosa ragionevole da fare ...
assylias

3
@assylias Un'applicazione autonoma lancerà un errore fatale a stderr.
Philip Whitehouse

36

Questa è una cattiva idea. In effetti, anche la cattura di Exceptionsolito è una cattiva idea. Consideriamo un esempio:

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

Ora, diciamo che getUserInput () si blocca per un po 'e un altro thread interrompe il thread nel peggior modo possibile (chiama thread.stop ()). Il tuo blocco cattura riceverà un ThreadDeatherrore. Questo è super cattivo. Il comportamento del codice dopo aver rilevato tale eccezione è in gran parte indefinito.

Un problema simile si verifica con la cattura di Exception. Forse getUserInput()non è riuscito a causa di un'eccezione InterruptException o di un'eccezione negata durante il tentativo di registrare i risultati o di tutti i tipi di altri errori. Non hai idea di cosa sia andato storto, perché per questo motivo non hai nemmeno idea di come risolvere il problema.

Hai tre opzioni migliori:

1 - Cattura esattamente le eccezioni che sai come gestire:

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

2 - Ripeti ogni eccezione che incontri e non sai come gestire:

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

3 - Usa un blocco finalmente in modo da non doverti ricordare di rilanciare:

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

4
+1 per aver affermato che anche catturare un'eccezione non va bene. Almeno c'è una ThreadInterructedException che richiede un'attenzione speciale (in breve - dopo averla catturata, devi impostare lo stato di interruzione del thread su "vero")
Kirill Gamazkov

So che è solo per illustrare le tue parole, ma penso che puoi controllare con un'espressione regolare se il tuo input utente è alfanumerico o quale formato ti serve e non usare try catch ovunque ogni volta.
amdev

Come faccio a sapere se c'è un errore / lanciabile se non lo prendo? Non ho visto niente nei log. App Java EE. Ho passato giorni bloccati senza sapere quale fosse il problema fino a quando non ho aggiunto questo problema.
Philip Rego

2
Considero l'opzione n. 2 una cattiva pratica. Immagina 10 chiamate concatenate con log & rethrow. Se guardi il file di registro, non sarai felice. L'eccezione viene registrata 10 volte rendendo i registri molto difficili da leggere. IMHO è molto meglio da fare throw new Exception("Some additional info, eg. userId " + userId, e);. Questo verrà registrato in una bella eccezione con 10 cause.
Petr Újezdský

21

Inoltre, tieni presente che quando prendi Throwable, puoi anche catturare, il InterruptedExceptionche richiede un trattamento speciale. Per ulteriori dettagli, vedere Gestione di InterructedException .

Se desideri rilevare solo eccezioni non verificate, potresti anche considerare questo modello

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

In questo modo, quando modifichi il tuo codice e aggiungi una chiamata al metodo che può generare un'eccezione controllata, il compilatore te lo ricorderà e poi potrai decidere cosa fare in questo caso.


14

direttamente dal javadoc della classe Error (che consiglia di non catturarli):

 * 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

Non è una cattiva pratica se non puoi assolutamente avere una bolla di eccezione da un metodo.

È una cattiva pratica se davvero non puoi gestire l'eccezione. Meglio aggiungere "lanci" alla firma del metodo piuttosto che prendere e rilanciare o, peggio, racchiuderlo in una RuntimeException e rilanciare.


10
Totalmente d'accordo: ci sono casi assolutamente legittimi per la gestione di tutte le Throwableistanze, ad esempio per la registrazione delle eccezioni personalizzata.
Yuriy Nakonechnyy

9

La cattura di Throwable a volte è necessaria se stai usando librerie che generano Errori in modo eccessivamente entusiasta, altrimenti la tua libreria potrebbe uccidere la tua applicazione.

Tuttavia, in queste circostanze sarebbe meglio specificare solo gli errori specifici generati dalla libreria, piuttosto che tutti i Throwable.


12
O usi una libreria scritta migliore?
Raedwald

6
In effetti, se hai la possibilità ;-)
DNA

questo è il problema più grande con la cattura degli oggetti lanciabili e il rilancio. rende davvero un'interfaccia impossibile per tutti i metodi a monte nello stack. Devono affrontare un lancio o una firma lanciabile inutilizzabile con cui gli altri dovranno fare i conti.
Andrew Norman

6

Throwable è la classe base per tutte le classi che possono essere lanciate (non solo le eccezioni). C'è poco che puoi fare se prendi un OutOfMemoryError o KernelError (vedi Quando catturare java.lang.Error? )

catturare le eccezioni dovrebbe essere sufficiente.


5

dipende dalla tua logica o per essere più specifico per le tue opzioni / possibilità. Se esiste un'eccezione specifica a cui puoi reagire in modo significativo, potresti prima intercettarla e farlo.

Se non c'è e sei sicuro che farai la stessa cosa per tutte le eccezioni e gli errori (ad esempio esci con un messaggio di errore), non è un problema catturare il throwable.

Di solito il primo caso vale e non prenderesti il ​​lanciabile. Ma ci sono ancora molti casi in cui catturarlo funziona bene.


4

Sebbene sia descritta come una pessima pratica, a volte potresti trovare rari casi in cui non solo è utile ma anche obbligatoria. Ecco due esempi.

In un'applicazione Web in cui è necessario mostrare all'utente una pagina di errore completa del significato. Questo codice assicura che ciò accada poiché è un grosso problema try/catchper tutti i gestori di richieste (servlet, azioni struts o qualsiasi controller ...)

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

}

Come altro esempio, considera di avere una classe di servizio che serve alle attività di trasferimento di fondi. Questo metodo restituisce un TransferReceiptse il trasferimento è stato eseguito o NULLse non è stato possibile.

String FoundtransferService.doTransfer( fundtransferVO);

Ora immaginando si ottiene un Listtrasferimento di fondi dall'utente e si deve utilizzare il servizio di cui sopra per fare tutto.

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

Ma che cosa accadrà se ogni eccezione accade? Non dovresti fermarti, poiché un trasferimento potrebbe essere andato a buon fine e uno no, dovresti continuare per tutti gli utenti Liste mostrare il risultato a ogni trasferimento. Quindi finisci con questo codice.

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

Puoi sfogliare molti progetti open source per vedere che throwableè davvero memorizzato nella cache e gestito. Per esempio qui è una ricerca di tomcat, struts2e 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
Ho visto il codice in questi collegamenti. Lanciabile non è solo quello che viene catturato! Ci sono anche altre eccezioni rilevate prima di Throwable.
developer1011

@ developer101 ovviamente, ma catturano throwable, ecco di cosa si tratta
Alireza Fattahi

4

La domanda è un po 'vaga; stai chiedendo "va bene catturare Throwable", o "va bene prendere Throwablee non fare niente"? Molte persone qui hanno risposto a quest'ultimo, ma questo è un problema secondario; Il 99% del tempo non si dovrebbe "consumare" o scartare l'eccezione, se si stanno recuperando Throwableo IOExceptiono qualsiasi altra cosa.

Se propaghi l'eccezione, la risposta (come la risposta a tante domande) è "dipende". Dipende da cosa stai facendo con l'eccezione, perché la stai catturando.

Un buon esempio del motivo per cui vorresti intercettare Throwableè fornire una sorta di pulizia in caso di errori. Ad esempio in JDBC, se si verifica un errore durante una transazione, potresti voler eseguire il rollback della transazione:

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

Notare che l'eccezione non viene scartata, ma propagata.

Ma come politica generale, catturare Throwableperché non hai una ragione e sei troppo pigro per vedere quali eccezioni specifiche vengono lanciate è una cattiva forma e una cattiva idea.


1

In generale, vuoi evitare di catturare Errors, ma posso pensare a (almeno) due casi specifici in cui è appropriato farlo:

  • Si desidera chiudere l'applicazione in risposta agli errori, in particolare AssertionError che è altrimenti innocuo.
  • Stai implementando un meccanismo di pool di thread simile a ExecutorService.submit () che richiede di inoltrare le eccezioni all'utente in modo che possano gestirle.

0

Se usiamo throwable , copre l' errore e basta.

Esempio.

    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();
}

}

Produzione:

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 è la superclasse di tutti gli errori e le eccezioni. Se usi Throwable in una clausola catch, non solo catturerà tutte le eccezioni, ma rileverà anche tutti gli errori. Gli errori vengono generati dalla JVM per indicare problemi seri che non devono essere gestiti da un'applicazione. Esempi tipici per questo sono OutOfMemoryError o StackOverflowError. Entrambi sono causati da situazioni che sono al di fuori del controllo dell'applicazione e non possono essere gestite. Quindi non dovresti catturare Throwable a meno che tu non sia abbastanza sicuro che sarà solo un'eccezione risiedere in Throwable.


-1

Sebbene sia generalmente una cattiva pratica catturare Throwable (come chiarito dalle numerose risposte a questa domanda), gli scenari in cui catturare Throwable è utile sono abbastanza comuni. Lasciatemi spiegare uno di questi casi che uso nel mio lavoro, con un esempio semplificato.

Considera un metodo che esegue l'aggiunta di due numeri e, dopo l'aggiunta riuscita, invia un avviso e-mail a determinate persone. Supponiamo che il numero restituito sia importante e utilizzato dal metodo chiamante.

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
    }
}

Il codice per inviare avvisi e-mail legge molte configurazioni di sistema e, quindi, potrebbero esserci una serie di eccezioni generate da quel blocco di codice. Ma non vogliamo che le eccezioni incontrate durante l'invio di avvisi si propaghino al metodo del chiamante, poiché quel metodo si occupa semplicemente della somma dei due valori Integer che fornisce. Quindi, il codice per inviare gli avvisi e-mail viene inserito in un try-catchblocco, dove Throwableviene catturato e le eventuali eccezioni vengono semplicemente registrate, consentendo al resto del flusso di continuare.


Cercherei di evitarlo avendo un thread dedicato al lavoro (con una coda) di invio di email.
boumbh

Suggerirei che questo è ancora un codice errato. Cattura Exceptions con tutti i mezzi, ma non Throwable.
andrewf
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.