È una cattiva pratica da catturare Throwable
?
Ad esempio qualcosa del genere:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
È una cattiva pratica o dovremmo essere il più precisi possibile?
È una cattiva pratica da catturare Throwable
?
Ad esempio qualcosa del genere:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
È una cattiva pratica o dovremmo essere il più precisi possibile?
Risposte:
Devi essere il più specifico possibile. In caso contrario, bug imprevisti potrebbero insinuarsi in questo modo.
Inoltre, anche le Throwable
copertine Error
e di solito non è un punto di ritorno . Non vuoi prenderlo / gestirlo, vuoi che il tuo programma muoia immediatamente in modo da poterlo riparare correttamente.
Questa è una cattiva idea. In effetti, anche la cattura di Exception
solito è 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 ThreadDeath
errore. 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);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Questo verrà registrato in una bella eccezione con 10 cause.
Inoltre, tieni presente che quando prendi Throwable
, puoi anche catturare, il InterruptedException
che 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.
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
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.
Throwable
istanze, ad esempio per la registrazione delle eccezioni personalizzata.
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.
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.
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.
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/catch
per 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 TransferReceipt
se il trasferimento è stato eseguito o NULL
se non è stato possibile.
String FoundtransferService.doTransfer( fundtransferVO);
Ora immaginando si ottiene un List
trasferimento 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 List
e 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
, struts2
e 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
throwable
, ecco di cosa si tratta
La domanda è un po 'vaga; stai chiedendo "va bene catturare Throwable
", o "va bene prendere Throwable
e 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 Throwable
o IOException
o 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 Throwable
perché non hai una ragione e sei troppo pigro per vedere quali eccezioni specifiche vengono lanciate è una cattiva forma e una cattiva idea.
In generale, vuoi evitare di catturare Error
s, ma posso pensare a (almeno) due casi specifici in cui è appropriato farlo:
AssertionError
che è altrimenti innocuo.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)
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.
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-catch
blocco, dove Throwable
viene catturato e le eventuali eccezioni vengono semplicemente registrate, consentendo al resto del flusso di continuare.
Exception
s con tutti i mezzi, ma non Throwable
.