Ist es eine schlechte Praxis zu fangen Throwable
?
Zum Beispiel so etwas:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Ist das eine schlechte Praxis oder sollten wir so spezifisch wie möglich sein?
Ist es eine schlechte Praxis zu fangen Throwable
?
Zum Beispiel so etwas:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Ist das eine schlechte Praxis oder sollten wir so spezifisch wie möglich sein?
Antworten:
Sie müssen so genau wie möglich sein. Andernfalls könnten unvorhergesehene Fehler auf diese Weise verschwinden.
Außerdem auch Throwable
Abdeckungen Error
und das ist normalerweise kein Punkt der Rückkehr . Sie möchten das nicht fangen / handhaben, Sie möchten, dass Ihr Programm sofort stirbt, damit Sie es richtig reparieren können.
Das ist eine schlechte Idee. In der Tat ist sogar das Fangen Exception
normalerweise eine schlechte Idee. Betrachten wir ein Beispiel:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
Nehmen wir nun an, getUserInput () blockiert für eine Weile und ein anderer Thread stoppt Ihren Thread auf die schlechteste Art und Weise (es ruft thread.stop () auf). Ihr catch-Block wird einen ThreadDeath
Fehler abfangen . Das ist super schlecht. Das Verhalten Ihres Codes nach dem Abfangen dieser Ausnahme ist weitgehend undefiniert.
Ein ähnliches Problem tritt beim Abfangen von Exception auf. Möglicherweise getUserInput()
fehlgeschlagen aufgrund einer InterruptException oder einer Ausnahme, bei der die Berechtigung verweigert wurde, während versucht wurde, die Ergebnisse zu protokollieren, oder aufgrund aller möglichen anderen Fehler. Sie haben keine Ahnung, was schief gelaufen ist, da Sie aus diesem Grund auch keine Ahnung haben, wie Sie das Problem beheben können.
Sie haben drei bessere Möglichkeiten:
1 - Fangen Sie genau die Ausnahmen ab, mit denen Sie umgehen können:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
2 - Wirf alle Ausnahmen zurück, auf die du stößt und die nicht zu handhaben sind:
try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
3 - Verwenden Sie einen finally-Block, damit Sie nicht daran denken müssen, erneut zu werfen:
Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Dies wird in einer schönen Ausnahme mit 10 Ursachen protokolliert.
Beachten Throwable
Sie auch, dass Sie beim Fangen auch fangen können, InterruptedException
was eine besondere Behandlung erfordert. Weitere Informationen finden Sie unter Umgang mit InterruptedException .
Wenn Sie nur ungeprüfte Ausnahmen abfangen möchten, können Sie dieses Muster auch berücksichtigen
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
Auf diese Weise erinnert Sie der Compiler daran, wenn Sie Ihren Code ändern und einen Methodenaufruf hinzufügen, der eine aktivierte Ausnahme auslösen kann, und Sie können dann entscheiden, was für diesen Fall zu tun ist.
direkt aus dem Javadoc der Error-Klasse (die empfiehlt, diese nicht zu fangen):
* 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
Es ist keine schlechte Praxis, wenn Sie absolut keine Ausnahmeblase aus einer Methode haben können.
Es ist eine schlechte Praxis, wenn Sie die Ausnahme wirklich nicht behandeln können. Es ist besser, der Methodensignatur "Würfe" hinzuzufügen, als sie nur zu fangen und erneut zu werfen oder, schlimmer noch, sie in eine RuntimeException zu verpacken und erneut zu werfen.
Throwable
Instanzen - z. B. für die benutzerdefinierte Ausnahmeprotokollierung.
Catching Throwable ist manchmal erforderlich, wenn Sie Bibliotheken verwenden, die Fehler mit Begeisterung auslösen. Andernfalls kann Ihre Bibliothek Ihre Anwendung beenden.
Unter diesen Umständen ist es jedoch am besten, nur die spezifischen Fehler anzugeben, die von der Bibliothek ausgelöst werden, und nicht alle Throwables.
Throwable ist die Basisklasse für alle Klassen, die ausgelöst werden können (nicht nur Ausnahmen). Sie können wenig tun, wenn Sie einen OutOfMemoryError oder KernelError abfangen (siehe Wann java.lang.Error abfangen? )
Ausnahmen zu fangen sollte ausreichen.
Dies hängt von Ihrer Logik ab oder davon, ob Sie Ihre Optionen / Möglichkeiten genauer kennen. Wenn es eine bestimmte Ausnahme gibt, auf die Sie möglicherweise sinnvoll reagieren können, können Sie sie zuerst abfangen und dies tun.
Wenn dies nicht der Fall ist und Sie sicher sind, dass Sie für alle Ausnahmen und Fehler dasselbe tun werden (z. B. mit einer Fehlermeldung beenden), ist es kein Problem, den Wurf zu fangen.
Normalerweise gilt der erste Fall und Sie würden den Wurf nicht fangen. Aber es gibt immer noch viele Fälle, in denen das Fangen gut funktioniert.
Obwohl es als sehr schlechte Praxis beschrieben wird, können Sie manchmal seltene Fälle finden, in denen es nicht nur nützlich, sondern auch obligatorisch ist. Hier sind zwei Beispiele.
In einer Webanwendung, in der Sie dem Benutzer eine vollständige Fehlerseite anzeigen müssen. Dieser Code stellt sicher, dass dies geschieht, da er sich try/catch
um alle Ihre Anforderungshandler (Servlets, Struts-Aktionen oder Controller) dreht.
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}}
Stellen Sie sich als weiteres Beispiel eine Serviceklasse vor, die dem Geldtransfergeschäft dient. Diese Methode gibt a zurück, TransferReceipt
wenn die Übertragung abgeschlossen ist oder NULL
nicht.
String FoundtransferService.doTransfer( fundtransferVO);
Im Imaging erhalten Sie eine List
Überweisung vom Benutzer, und Sie müssen den oben genannten Service verwenden, um alle Transaktionen durchzuführen.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
Aber was passiert, wenn eine Ausnahme auftritt? Sie sollten nicht aufhören, da eine Übertragung möglicherweise erfolgreich war und eine nicht. Sie sollten alle Benutzer weiter verfolgen List
und das Ergebnis jeder Übertragung anzeigen. Sie erhalten also diesen Code.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
Sie können viele Open Source-Projekte durchsuchen, um festzustellen, ob das throwable
wirklich zwischengespeichert und verarbeitet ist. Zum Beispiel ist hier eine Suche nach tomcat
, struts2
und 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
, worum es in dieser Frage geht
Die Frage ist etwas vage; Fragen Sie "Ist es in Ordnung zu fangen Throwable
" oder "Ist es in Ordnung, einen zu fangen Throwable
und nichts zu tun"? Viele Leute hier antworteten auf Letzteres, aber das ist ein Nebenproblem; 99% der Zeit , die Sie sollten nicht „verbrauchen“ oder die Ausnahme verwerfen, ob Sie fangen Throwable
oder IOException
oder was auch immer.
Wenn Sie die Ausnahme verbreiten, lautet die Antwort (wie die Antwort auf so viele Fragen) "es kommt darauf an". Es hängt davon ab, was Sie mit der Ausnahme tun - warum Sie es fangen.
Ein gutes Beispiel dafür, warum Sie abfangen möchten, Throwable
ist die Bereitstellung einer Bereinigung, wenn ein Fehler auftritt. Wenn beispielsweise in JDBC während einer Transaktion ein Fehler auftritt, möchten Sie die Transaktion zurücksetzen:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Beachten Sie, dass die Ausnahme nicht verworfen, sondern weitergegeben wird.
Aber als allgemeine Richtlinie ist das Fangen, Throwable
weil Sie keinen Grund haben und zu faul sind, um zu sehen, welche spezifischen Ausnahmen ausgelöst werden, eine schlechte Form und eine schlechte Idee.
Im Allgemeinen möchten Sie vermeiden, Error
s zu fangen, aber ich kann mir (mindestens) zwei spezielle Fälle vorstellen, in denen dies angebracht ist:
AssertionError
diese ansonsten harmlos sind.Wenn wir Throwable verwenden , deckt es auch Error ab und das wars.
Beispiel.
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();
}
}}
Ausgabe:
java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
Wurfbar ist die Oberklasse aller Fehler und Erschöpfungen. Wenn Sie Throwable in einer catch-Klausel verwenden, werden nicht nur alle Ausnahmen, sondern auch alle Fehler abgefangen. Die JVM gibt Fehler aus, um auf schwerwiegende Probleme hinzuweisen, die nicht von einer Anwendung behandelt werden sollen. Typische Beispiele hierfür sind der OutOfMemoryError oder der StackOverflowError. Beides wird durch Situationen verursacht, die außerhalb der Kontrolle der Anwendung liegen und nicht behandelt werden können. Sie sollten Throwables also nicht fangen, es sei denn, Sie sind ziemlich sicher, dass es sich nur um eine Ausnahme in Throwable handelt.
Während es im Allgemeinen eine schlechte Praxis ist, Throwable zu fangen (wie aus den zahlreichen Antworten auf diese Frage hervorgeht), sind die Szenarien, in denen das Fangen Throwable
nützlich ist, weit verbreitet. Lassen Sie mich einen solchen Fall, den ich bei meiner Arbeit verwende, anhand eines vereinfachten Beispiels erläutern.
Stellen Sie sich eine Methode vor, bei der zwei Zahlen hinzugefügt werden und nach erfolgreicher Hinzufügung eine E-Mail-Benachrichtigung an bestimmte Personen gesendet wird. Angenommen, die zurückgegebene Nummer ist wichtig und wird von der aufrufenden Methode verwendet.
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
}
}
Der Code zum Senden von E-Mail-Warnungen liest viele Systemkonfigurationen und daher kann es eine Vielzahl von Ausnahmen geben, die von diesem Codeblock ausgelöst werden. Wir möchten jedoch nicht, dass eine Ausnahme, die während des Alarmversands auftritt, an die Aufrufermethode weitergegeben wird, da diese Methode lediglich die Summe der beiden bereitgestellten Ganzzahlwerte betrifft. Daher wird der Code zum Versenden von E-Mail-Benachrichtigungen in einem try-catch
Block abgelegt, in dem Throwable
alle Ausnahmen abgefangen und protokolliert werden, sodass der Rest des Flusses fortgesetzt werden kann.
Exception
auf jeden Fall, aber nicht Throwable
.