Ist es eine schlechte Praxis, Throwable zu fangen?

Antworten:

104

Sie müssen so genau wie möglich sein. Andernfalls könnten unvorhergesehene Fehler auf diese Weise verschwinden.

Außerdem auch ThrowableAbdeckungen Errorund 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.

BalusC
quelle
38
Es gibt Situationen, in denen es angemessen ist, Fehler abzufangen und fortzufahren. Beispiel: Wenn Sie in einem Servlet einen OutOfMemoryError enconterieren, weil eine bestimmte Anforderung den gesamten Speicher belegt, können Sie versuchen, fortzufahren, da die Objekte nach der Verarbeitung der Anforderung GC sind. Gleiches gilt für einen Assertionsfehler. Sie fahren eine Anwendung nicht herunter, weil bei einer Anforderung ein Fehler aufgetreten ist.
Gawi
7
Woher wissen Sie, was zugewiesen wurde und was nicht vor dem OOME? Alle Wetten sind ungültig, sobald Sie diese erhalten, auch in einem J2EE-Container wie Tomcat oder JBoss.
Bmauter
10
Wir hatten NoSuchMethodError und haben dankenswerterweise nicht alle unsere Kunden durch Herunterfahren des Servers ausgeschaltet, wie dies zwei Wochen nach der Bereitstellung geschehen ist. dh. Wir haben immer einen Haken, der Throwable fängt und sich bemüht, Fehler zu behandeln und an den Kunden zu senden. Schließlich gibt es viele Arten von Fehlern, die behoben werden können, da sie möglicherweise nur 1 von 1000 Kunden betreffen.
Dean Hiller
10
" Sie möchten, dass Ihr Programm sofort stirbt, damit Sie es richtig reparieren können " => Wenn Ihr Programm stirbt, woher wissen Sie, was passiert ist? Das Abfangen von Throwable / Error, um das Problem zu protokollieren, ist eine vernünftige Sache ...
Assylias
3
@assylias Eine eigenständige Anwendung gibt einen schwerwiegenden Fehler an stderr aus.
Philip Whitehouse
36

Das ist eine schlechte Idee. In der Tat ist sogar das Fangen Exceptionnormalerweise 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 ThreadDeathFehler 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);
 }
Brandon Yarbrough
quelle
4
+1 für die Feststellung, dass es nicht gut ist, eine Ausnahme zu erwischen. Zumindest gibt es eine ThreadInterruptedException, die besondere Sorgfalt erfordert (kurz gesagt - nachdem Sie sie abgefangen haben, müssen Sie den unterbrochenen Status des Threads wieder auf "true" setzen)
Kirill Gamazkov
Ich weiß, es dient nur zur Veranschaulichung Ihrer Worte, aber ich denke, Sie können mit einem regulären Ausdruck überprüfen, ob Ihre Benutzereingabe alphanumerisch ist oder welches Format Sie benötigen, und nicht jedes Mal versuchen, catch überall zu verwenden.
Amdev
Wie soll ich wissen, ob es einen Fehler / Throwable gibt, wenn ich ihn nicht fange? Ich habe nichts in den Protokollen gesehen. Java EE App. Ich habe nur Tage damit verbracht, nicht zu wissen, was das Problem war, bis ich diesen Haken hinzugefügt habe.
Philip Rego
2
Ich betrachte die Option 2 als schlechte Praxis. Stellen Sie sich 10 verkettete Anrufe mit log & rethrow vor. Wenn Sie sich die Protokolldatei ansehen, werden Sie nicht glücklich sein. Die Ausnahme wird zehnmal protokolliert, wodurch Protokolle sehr schwer lesbar sind. IMHO viel besser ist zu tun throw new Exception("Some additional info, eg. userId " + userId, e);. Dies wird in einer schönen Ausnahme mit 10 Ursachen protokolliert.
Petr Újezdský
21

Beachten ThrowableSie auch, dass Sie beim Fangen auch fangen können, InterruptedExceptionwas 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.

gawi
quelle
14

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
Andrew Norman
quelle
13

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.

Duffymo
quelle
10
Stimmen Sie voll und ganz zu - es gibt absolut legitime Fälle für die Behandlung aller ThrowableInstanzen - z. B. für die benutzerdefinierte Ausnahmeprotokollierung.
Yuriy Nakonechnyy
9

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.

DNA
quelle
12
Oder eine besser geschriebene Bibliothek verwenden?
Raedwald
6
In der Tat, wenn Sie die Wahl haben ;-)
DNA
Das ist das größte Problem beim Werfen und erneuten Werfen von Wurfgegenständen. Es macht wirklich eine unmögliche Schnittstelle für alle Methoden im Stack. Sie müssen sich entweder mit einem Wurf oder mit einer unbrauchbaren Wurfsignatur auseinandersetzen, mit der sich andere befassen müssen.
Andrew Norman
6

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.

ic3
quelle
5

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.

b.buchhold
quelle
4

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/catchum 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, TransferReceiptwenn die Übertragung abgeschlossen ist oder NULLnicht.

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 Listund 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 throwablewirklich zwischengespeichert und verarbeitet ist. Zum Beispiel ist hier eine Suche nach tomcat, struts2und 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

Alireza Fattahi
quelle
1
Sah den Code in diesen Links. Werfen ist nicht nur derjenige, der gefangen wird! Es gibt auch andere Ausnahmen, die vor Throwable gefangen wurden.
Entwickler1011
@ Entwickler101 natürlich, aber sie fangen throwable, worum es in dieser Frage geht
Alireza Fattahi
4

Die Frage ist etwas vage; Fragen Sie "Ist es in Ordnung zu fangen Throwable" oder "Ist es in Ordnung, einen zu fangen Throwableund 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 Throwableoder IOExceptionoder 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, Throwableist 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, Throwableweil Sie keinen Grund haben und zu faul sind, um zu sehen, welche spezifischen Ausnahmen ausgelöst werden, eine schlechte Form und eine schlechte Idee.

Garret Wilson
quelle
1

Im Allgemeinen möchten Sie vermeiden, Errors zu fangen, aber ich kann mir (mindestens) zwei spezielle Fälle vorstellen, in denen dies angebracht ist:

  • Sie möchten die Anwendung als Reaktion auf Fehler herunterfahren, insbesondere wenn AssertionError diese ansonsten harmlos sind.
  • Implementieren Sie einen Thread-Pooling-Mechanismus ähnlich ExecutorService.submit () , bei dem Sie Ausnahmen an den Benutzer zurückleiten müssen, damit dieser damit umgehen kann.
Gili
quelle
0

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)
VicXj
quelle
0

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.

Sidath Bhanuka Randeniya
quelle
-1

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 Throwablenü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-catchBlock abgelegt, in dem Throwablealle Ausnahmen abgefangen und protokolliert werden, sodass der Rest des Flusses fortgesetzt werden kann.

CodeNewbie
quelle
Ich würde versuchen, dies zu vermeiden, indem ich einen Thread für den Job (mit einer Warteschlange) zum Senden von E-Mails habe.
Boumbh
Ich würde vorschlagen, dass dies immer noch schlechter Code ist. Fangen Sie Exceptionauf jeden Fall, aber nicht Throwable.
Andrewf