Ich bekomme immer wieder die gefürchteten java.something.someException Fehler, während ich meine Java App laufe. und ich habe nicht den Eindruck, welche Ausnahmen zu handhaben sind und was nicht?
Wenn ich die API-Dokumente lese, werfen die meisten Funktionen Ausnahmen auf, wie wenn ich I / O verwende oder ein Array verwende ... etc.
Wie kann man entscheiden, welche Ausnahmen zu fangen sind und welche nicht und auf welchen Parametern basieren?
Ich spreche hier über geprüfte Ausnahmen.
Kurze Antwort
Fangen Sie Ausnahmen ein, mit denen Sie dann und dort umgehen können, und werfen Sie erneut was Sie nicht können.
Lange Antwort
Es heißt Ausnahmebehandlung -Code aus einem Grund: Immer wenn Sie versucht werden, einen catch
-Block zu schreiben, müssen Sie einen guten Grund haben, die Exception überhaupt zu fangen. Ein catch
-Block gibt an, dass Sie die Ausnahme abfangen wollen, und dann etwas dagegen tun . Beispiele für , die etwas dagegen tun umfassen, sind aber nicht beschränkt auf:
Erneuter Versuch der Operation, die die Ausnahme ausgelöst hat. Dies kann im Fall von IOException
und anderen Problemen sinnvoll sein, die vorübergehend sein können (z. B. ein Netzwerkfehler beim Versuch, eine Datei auf einen Server hochzuladen. Möglicherweise sollte Ihr Code den Upload einige Male wiederholen).
Protokolliert die Ausnahme. Ja, das Protokollieren zählt als etwas zu tun. Sie können die ursprüngliche Ausnahme auch nach der Protokollierung erneut auslösen, damit anderer Code immer noch die Möglichkeit hat, mit der Ausnahme umzugehen. Dies hängt jedoch von der Situation ab.
Verpacken Sie die Ausnahme in einer anderen Ausnahme, die für die Schnittstelle Ihrer Klasse besser geeignet ist. Wenn Sie beispielsweise eine FileUploader
-Klasse haben, können Sie IOException
in eine allgemeinere UploadFailedException
umbrechen, so dass Klassen, die Ihre Klasse verwenden, keine detaillierte Kenntnis davon haben müssen, wie Ihr Upload-Code funktioniert (die Tatsache dass es ein IOException
wirft ist technisch ein Implementierungsdetail).
Wenn der Code an dem Punkt, an dem er auftritt, keine vernünftigen Maßnahmen ergreifen kann, sollten Sie ihn überhaupt nicht abfangen.
Leider funktionieren solche hartnäckigen Regeln niemals zu 100%. Manchmal wirft eine von Ihnen verwendete Bibliothek eines Drittanbieters geprüfte Ausnahmen, die Sie nicht interessieren oder die nie wirklich passieren werden. In diesen Fällen können Sie einen leeren catch
-Block verwenden, der keinen Code ausführt, aber dies ist keine empfohlene Methode, um mit Ausnahmen umzugehen. Zumindest sollten Sie einen Kommentar hinzufügen, der erklärt, warum Sie die Ausnahme ignorieren (aber als CPerkins Notizen in den Kommentaren, "Niemals niemals sagen." Möglicherweise möchten Sie diese Art von "Nothing-to-have" -Ausnahmen tatsächlich protokollieren, damit Sie, falls eine solche Ausnahme auftritt , dies bemerken und können untersuchen Sie weiter).
Dennoch lautet die allgemeine Regel, wenn die Methode, in der Sie sich befinden, mit einer Ausnahme nichts Vernünftiges tun kann (protokollieren Sie es, wiederholen Sie es, wiederholen Sie die Operation usw.), dann sollten Sie keinen catch
-Block schreiben überhaupt. Lassen Sie die aufrufende Methode mit der Ausnahme umgehen. Wenn Sie mit checked exceptions arbeiten, fügen Sie die checked exception zur throws
-Klausel Ihrer Methode hinzu, die den Compiler anweist, die Exception an die aufrufende Methode weiterzugeben, die möglicherweise besser geeignet ist, um den Fehler zu behandeln (die aufrufende Methode kann) haben mehr Kontext, so könnte es eine bessere Idee haben, wie man mit der Ausnahme umgehen).
Normalerweise ist es sinnvoll, ein try...catch
in Ihre main
-Methode einzufügen, das alle Ausnahmen abfängt, mit denen Ihr Code nicht umgehen konnte, und diese Informationen dem Benutzer zu melden und die Anwendung ordnungsgemäß zu beenden.
Und vergessen Sie schließlich nicht finally
Denken Sie auch daran, dass Sie auch dann, wenn Sie keinen catch
-Block schreiben, immer noch einen finally
-Block schreiben müssen, wenn Sie Reinigungscode benötigen, um ihn auszuführen, unabhängig davon, ob der Vorgang gerade ausgeführt wird zum Ausführen wirft eine Ausnahme oder nicht. Ein gängiges Beispiel ist das Öffnen einer Datei im Block try
: Sie möchten die Datei trotzdem schließen, selbst wenn eine Ausnahme auftritt und auch wenn Ihre Methode die Ausnahme nicht abfangen kann. Eine weitere gängige Faustregel, die Sie vielleicht in Tutorials und Büchern sehen, ist, dass try...finally
blocks häufiger vorkommen als try...catch
blocks in Ihrem Code, genau weil catch
blocks nur geschrieben werden sollten, wenn Sie tatsächlich damit umgehen können Die Ausnahme, aber finally
Blöcke werden benötigt, wenn Ihr Code nach sich selbst bereinigen muss.
Ich empfehle Kapitel 9 (Ausnahmen) in Joshua Blochs Effektives Java, 2. Ausgabe für diese Fragen.
Eine allgemeine Faustregel besagt, dass Sie mit den Ausnahmen umgehen, für die Sie etwas tun können, und nicht mit denen, die nicht möglich sind. In Fällen, in denen Sie keine Ausnahme behandeln, wird erwartet, dass der Aufrufer sie verarbeitet. Wenn Sie normalerweise ein Framework oder eine Bibliothek schreiben, werden Sie am Ende Low-Level-Exceptions wie SQLException
in eine Bibliothek oder eine Framework-spezifische Exception einbetten, um die Details auf der unteren Ebene wegzuspulen.
Wenn Sie beispielsweise eine Methode schreiben, die in eine Datei auf der Festplatte schreibt, sollten Sie wahrscheinlich FileNotFoundException
s verarbeiten, da Sie die fehlende Datei erstellen können, aber wenn Sie Probleme beim Erstellen der Datei oder beim Schreiben haben Dann sollten Sie den Anrufer wahrscheinlich davon abhalten lassen (nachdem Sie die erforderlichen Bereinigungsarbeiten durchgeführt haben).
Das sind meine persönlichen Erkenntnisse:
Und ein Hinweis, der Ihnen eines Tages helfen wird: Wenn Sie abstürzen, stellen Sie ein einfaches Mittel bereit, damit der Benutzer die Nachricht per E-Mail an den Stack-Trace senden kann.
EDIT: Haben Sie keine Angst, neue Ausnahmen zu erstellen, wenn die verfügbaren nicht vollständig abdecken, was Sie brauchen. Dies ermöglicht Ihnen eine bessere Benennung in Stack-Traces und Ihr Fehlerbehandlungscode kann leicht zwischen verschiedenen Fällen unterscheiden. Möglicherweise möchten Sie alle eigenen Ausnahmen auf einer gemeinsamen Basisklasse ("OurDomainException") basieren, so dass Sie die Basisklasse in catch-Klauseln verwenden können, um festzustellen, um welchen Typ es sich handelt.
Nachdem ich ein paar Jahre in Java programmiert habe, stimme ich zu, dass es leicht ist, sich zu ärgern, wenn man endlose Try-Catch-Blöcke schreibt.
Eine gute Möglichkeit, schnell von Codierung ist besondere Ausnahmen in so niedrigen Niveau zu fangen, wie Sie können, und die Basis Exception
an der äußersten Ebene zu fangen (in main()
) nur so, dass Sie eine generische Fehlermeldung schreiben kann stattdessen das Programm abstürzen zu lassen.
Damit können Sie schnell einen laufenden Code haben, und dann können Sie sich Zeit nehmen, um bestimmte Ausnahmen in verschiedenen Layern mit ihrer Bearbeitungslogik hinzuzufügen.
Catch aktiviert Exception
, nicht RuntimeException
. Versuchen Sie, eine bestimmte Ausnahme zu fangen, versuchen Sie nicht, nach generischem java.lang.Exception
zu fangen.
Ich erhalte auch Ausnahmen für sauberere Modulgrenzen. Wenn beispielsweise eine SQLException ausgelöst wird, die ich nicht verarbeiten kann, werde ich sie dennoch abfangen und stattdessen eine eigene beschreibende Ausnahme auslösen (die SQLException als cause setzen). Auf diese Weise muss der Anrufer nicht wissen, dass ich eine Datenbank verwende, um seine Anfrage zu erfüllen. Er erhält nur einen Fehler "Kann diesen speziellen Anwendungsfall nicht behandeln". Wenn ich mich entscheide, seine Anfrage ohne Datenbankzugriff zu erfüllen, muss ich meine API nicht ändern.
Als Faustregel sehe ich Ausnahmen, bei denen ich etwas machen kann, oder wo ich möchte, dass die Ausnahme aufhört, sich zu bewegen. Zum Beispiel, wenn ich eine Liste von Elementen verarbeite, und ich will, dass das nächste Element verarbeitet wird, selbst wenn die anderen fehlschlagen, dann werde ich versuchen ... einen Block um diese Elementverarbeitung und nur dort zu fangen. Ich versuche normalerweise ... catch (Throwable) in der Hauptmethode meines Programms, damit ich Fehler wie eine nicht gefundene Klasse oder ähnliches protokollieren kann.
Ich versuche es nicht ... fange Block in einer Methode, wenn ich nicht weiß, was ich mit dieser Ausnahme machen soll. Wenn Sie dies tun, werden Sie nur Ihren Code mit viel Ausnahmebehandlungscode aufblähen.