Laufzeitausnahme in Java-Anwendung auslösen

11

Ich arbeite als Auftragnehmer und entwerfe Java-Unternehmensanwendungen für meinen Kunden als technischer Leiter. Die Anwendung wird von Endbenutzern verwendet und es wird ein Support-Team geben, das die Anwendung unterstützt, wenn wir sie verlassen.

Die anderen technischen Leads, mit denen ich zusammenarbeite, haben den Eindruck, dass die Ausnahmebehandlung den Code verschmutzt. Das System sollte nur aktivierte Ausnahmen von der Serviceebene auslösen, und der Rest des Codes sollte Laufzeitausnahmen von allen anderen Ebenen auslösen, damit die nicht aktivierten Ausnahmen nicht behandelt werden müssen.

Was ist die Notwendigkeit, jemals eine ungeprüfte Ausnahme in einer Geschäftsanwendung auszulösen?

Aus meiner Erfahrung in der Vergangenheit über Laufzeitausnahmen:

1) Nicht aktivierte Ausnahmen machen den Code unvorhersehbar, da sie selbst im Javadoc nicht angezeigt werden.

2) Das Auslösen von nicht aktivierten Ausnahmen in einer Geschäftsanwendung ist nicht sinnvoll, da Sie dem Benutzer erklären, wenn Sie sie auslösen und sie direkt auf das Gesicht des Benutzers übertragen. Ich habe genug von Webanwendungen gesehen, die zeigen, 500 -Internal Error. Contact Administratorwas für einen Endbenutzer oder das Support-Team, das die Anwendung verwaltet, nichts bedeutet.

3) Durch das Auslösen von Laufzeitausnahmen werden die Benutzer der Klasse, die die Ausnahme auslösen, gezwungen, den Quellcode zu durchlaufen, um zu debuggen und festzustellen, warum die Ausnahme ausgelöst wird. Dies kann nur vermieden werden, wenn das Javadoc der Laufzeitausnahme hervorragend dokumentiert ist, was meiner Meinung nach niemals der Fall ist.

RandominstanceOfLivingThing
quelle
Können Sie erklären, was Service Tier ist? Ist die Ebene am weitesten vom Benutzer entfernt oder dem Benutzer am nächsten?
Manoj R
Warum passt diese Frage nicht auf SO?
Bjarke Freund-Hansen
@ Manoj R: Weder noch. Auf einer Serviceebene oder -ebene werden Geschäftsregeln und -logik ausgeführt, die sowohl von Details der DB-Technologie als auch von der Client-Präsentation getrennt sind.
Michael Borgwardt
6
Ungeprüfte Ausnahmen [...] werden auch im Javadoc nicht angezeigt. => Sie werden angezeigt, wenn Sie sie mit dem @throwsTag dokumentieren. Dies ist übrigens eine gute Vorgehensweise.
Assylias
4
@SureshKoya Effektives Java, Punkt 62: Dokumentieren Sie alle Ausnahmen, die von jeder Methode ausgelöst werden. Extrahieren: Verwenden Sie das Javadoc- @throwsTag, um jede nicht aktivierte Ausnahme zu dokumentieren, die eine Methode auslösen kann. Verwenden Sie jedoch nicht das Schlüsselwort throw, um nicht aktivierte Ausnahmen in die Methodendeklaration aufzunehmen.
Assylias

Antworten:

12

Überprüfte Ausnahmen sind ein fehlgeschlagener Ausdruck im Sprachdesign. Sie zwingen Sie zu extrem undichten Abstraktionen und schmutzigem Code. Sie sollten so weit wie möglich vermieden werden.

Wie für Ihre Punkte:

1) Überprüfte Ausnahmen machen den Code schmutzig und nicht weniger unvorhersehbar, da sie überall angezeigt werden .

2) Wie wird dem Benutzer eine aktivierte Ausnahme besser angezeigt? Der einzige wirkliche Unterschied zwischen aktivierten und nicht aktivierten Ausnahmen ist technischer Natur und betrifft nur den Quellcode.

3) Schon mal was von Stapelspuren gehört? Sie sagen Ihnen genau, wo die Ausnahme ausgelöst wurde, unabhängig davon, ob sie aktiviert oder deaktiviert ist. Tatsächlich sind geprüfte Ausnahmen beim Debuggen in der Regel schlechter, da sie häufig umbrochen werden, was zu längeren und hässlicheren Stapelspuren führt, oder sogar zusammen verloren gehen, weil das Umbrechen falsch durchgeführt wurde.

Es gibt zwei Arten von Ausnahmen: solche, die "normal" auftreten und normalerweise sehr nahe an ihrem Ort behandelt werden, und solche, die wirklich außergewöhnlich sind und generisch in einer sehr hohen Ebene behandelt werden können (brechen Sie einfach die aktuelle Aktion ab und protokollieren Sie sie /) Fehler anzeigen).

Überprüfte Ausnahmen waren ein Versuch, diese Unterscheidung an dem Punkt, an dem die Ausnahmen definiert sind, in die Sprachsyntax aufzunehmen. Die Probleme damit sind

  • Die Unterscheidung liegt wirklich beim Aufrufer , nicht beim Code, der die Ausnahme auslöst
  • Es ist völlig orthogonal zur semantischen Bedeutung einer Ausnahme, aber wenn Sie es an die Klassenhierarchie binden, müssen Sie beide mischen
  • Der springende Punkt bei Ausnahmen ist, dass Sie entscheiden können, auf welcher Ebene sie abgefangen werden sollen, ohne das Risiko einzugehen, einen Fehler stillschweigend zu verlieren oder den Code auf mittleren Ebenen zu verschmutzen. geprüfte Ausnahmen verlieren diesen zweiten Vorteil.
Michael Borgwardt
quelle
1. Nun, im Falle einer nicht aktivierten Ausnahme wissen Sie nicht einmal, ob ein Anruf zu einer nicht aktivierten führen könnte. 2. Als Sie sagten "Es ist völlig orthogonal zur semantischen Bedeutung einer Ausnahme, aber wenn Sie es an die Klassenhierarchie binden, müssen Sie die beiden mischen", meinten Sie vermutlich Aufrufhierarchie anstelle von Klassenhierarchie.
RandominstanceOfLivingThing
2
@Suresh Koya: Nein, ich meinte Klassenhierarchie. Die Tatsache, dass eine Deklaration SQLException extends Exceptionbedeutet, dass die Auswahl eines Auslösers, SQLExceptionweil ein Fehler mit dem DB-Zugriff zu tun hat, automatisch bedeutet, dass die Ausnahme überprüft wird. Und Sie können ungeprüfte Ausnahmen in Javadoc-Kommentaren problemlos dokumentieren. Funktioniert gut für alle anderen Sprachen.
Michael Borgwardt
1
@MichaelBorgwardt "Überprüfte Ausnahmen sind ein fehlgeschlagener Ausdruck im Sprachdesign", ist es Ihre persönliche Meinung, wenn nicht, würde ich gerne mehr darüber lesen. Jeder authentische Link wäre eine große Hilfe.
Vipin
2
@Vipin: Es ist meine persönliche Meinung, aber eine, die viele andere Menschen teilen, z. B. Bruce Eckel: mindview.net/Etc/Discussions/CheckedExceptions - und die Tatsache, dass in den 20 Jahren seit Einführung von Java keine andere Sprache geprüfte Ausnahmen übernommen hat, spricht für sich selbst ...
Michael Borgwardt
2

Meiner Ansicht nach hängt es davon ab, was Ihr Code tut, welche Art von Ausnahme ausgelöst wird.

Ich werfe geprüfte Ausnahmen aus, wenn ich erwarte, dass sie ziemlich häufig auftreten können (z. B. Benutzer, die zweifelhafte Daten eingeben), und ich erwarte, dass der aufrufende Code die Situation behandelt.

Ich löse ungeprüfte / Laufzeitausnahmen (nicht so oft wie aktiviert), wenn ich eine seltene Situation habe, von der ich nicht erwarte, dass der aufrufende Code sie behandelt. Ein Beispiel könnte ein seltsamer speicherbezogener Fehler sein, von dem ich nie erwarte, dass er auftritt. Deaktiviert sind die Arten von Fehlern, von denen Sie erwarten, dass sie die Anwendung zum Absturz bringen.

Ohne Unterstützung sollte keine Ausnahme vor einem Benutzer angezeigt werden. Auch wenn es nur ein "Bitte schneiden Sie diesen Fehler-Dump aus und fügen Sie ihn in eine E-Mail ein". Nichts ist für einen Benutzer ärgerlicher, als wenn ihm mitgeteilt wird, dass ein Fehler vorliegt, aber keine Details oder Maßnahmen angegeben werden, die er ergreifen kann, um die Behebung des Fehlers einzuleiten.

Ich vermute, dass die von Ihnen erwähnte Philosophie aus einer von zwei Quellen stammt:

  • Faule Programmierer, die versuchen, Arbeit zu vermeiden.
  • Oder Entwickler, die Code unterstützen mussten, der in die andere Richtung ging. dh Überfehlerbehandlung. Die Art von Code, die große Mengen an Ausnahmebehandlung enthält, von denen viele nichts oder noch schlimmer nichts bewirken, wird zur Flusskontrolle und für andere falsche Zwecke verwendet.
drekka
quelle
Wenn etwas ziemlich oft passieren kann, ist es keine Ausnahme. Aber vereinbart, dass Fehler nicht geworfen werden sollten, von denen der Benutzer keine Ahnung hat.
Manoj R
Ich stimme Ihrer Philosophie zu. Was ist für eine Anwendung, die Sie für Benutzer entwickeln, erforderlich, um eine Laufzeitausnahme auszulösen?
RandominstanceOfLivingThing
Auch wenn eine Runtime-Ausnahme ausgelöst wird, bevorzuge ich die Konvention @assylias, auf die verwiesen wird.
RandominstanceOfLivingThing
1

Wenn Sie keine ungeprüften Laufzeitausnahmen wünschen, warum verwenden Sie dann Java? Es gibt fast überall die Möglichkeit von Laufzeitausnahmen - insbesondere NullPointerException-, ArithmeticException-, ArrayIndexOutOfBounds-Ausnahmen usw.

Und wenn Sie einmal die Protokolldateien eines J2EE-Systems gelesen haben, wie zum Beispiel eine SAP NetWeaver-Installation, werden Sie feststellen, dass solche Ausnahmen buchstäblich immer auftreten.

Ingo
quelle
Aus der Java-Sprachspezifikation: "Die Laufzeitausnahmeklassen (RuntimeException und ihre Unterklassen) sind von der Überprüfung der Kompilierungszeit ausgenommen, da nach Ansicht der Entwickler der Java-Programmiersprache die Erklärung solcher Ausnahmen nicht wesentlich zur Feststellung der Richtigkeit beitragen würde Viele der Operationen und Konstrukte der Java-Programmiersprache können zu Laufzeitausnahmen führen. "
RandominstanceOfLivingThing
1
Die Laufzeitausnahme, von der Sie sprechen, sind diejenigen, die sich aus Java ergeben, da das Java-Laufzeitsystem keine Ahnung hat, warum Sie versuchen, den bestimmten Aufruf auszuführen. Beispiel: Eine NullPointerException würde auftreten, wenn Sie eine Methode aufrufen, die null ist. In diesem Fall hat das Java-Laufzeitsystem kein richtiges Wort für die Torheit des Programmierers und würde den Aufrufer mit einer NullPointerException schlagen. Der traurige Teil ist, dass der Anrufer das Netweaver-Produkt an Sie verkauft hat und Sie als Benutzer nun Opfer einer schlechten Programmierung sind. Die Tester sind gleichermaßen schuld, da sie nicht alle Grenztests durchgeführt haben.
RandominstanceOfLivingThing
1

Es gibt einige Regeln für die Ausnahmebehandlung, die Sie berücksichtigen sollten. Zunächst müssen Sie jedoch berücksichtigen, dass Ausnahmen Teil der vom Code bereitgestellten Schnittstelle sind . dokumentieren sie . Dies ist natürlich besonders wichtig, wenn es sich um eine öffentliche Schnittstelle handelt, aber auch bei privaten Schnittstellen ist dies eine sehr gute Idee.

Ausnahmen sollten nur an dem Punkt behandelt werden, an dem der Code etwas Sinnvolles damit anfangen kann. Die schlechteste Handhabungsoption besteht darin, überhaupt nichts dagegen zu unternehmen, was nur getan werden sollte, wenn dies genau die richtige Option ist. (Wenn ich eine solche Situation in meinem Code habe, füge ich einen entsprechenden Kommentar hinzu, damit ich mir keine Sorgen um den leeren Körper machen muss.)

Die zweitschlechteste Option besteht darin, eine nicht verwandte Ausnahme auszulösen, ohne dass das Original als Ursache beigefügt ist. Das Problem hierbei ist, dass die Informationen innerhalb der ursprünglichen Ausnahme, die die Diagnose des Problems ermöglichen würden, verloren gehen. Sie erstellen etwas, mit dem niemand etwas anfangen kann (außer sich darüber zu beschweren, dass „es nicht funktioniert“ und wir alle wissen, wie wir diese Fehlerberichte hassen ).

Viel besser ist es, die Ausnahme zu protokollieren. Auf diese Weise kann jemand herausfinden, wo das Problem liegt, und es beheben. Sie sollten die Ausnahme jedoch nur an dem Punkt protokollieren, an dem sie sonst verloren gehen oder über eine externe Verbindung gemeldet werden würde. Dies liegt nicht daran, dass die Protokollierung häufiger ein großes Problem darstellt, sondern daran, dass eine übermäßige Protokollierung bedeutet, dass das Protokoll nur mehr Speicherplatz beansprucht, ohne mehr Informationen zu enthalten. Sobald Sie die Ausnahme protokolliert haben, können Sie dem Benutzer / Client mit gutem Gewissen eine Précis melden (sofern Sie auch den Zeitpunkt der Generierung - oder eine andere Korrelationskennung - in diesen Bericht aufnehmen, damit die Kurzversion abgeglichen werden kann ggf. mit dem Detail).

Die beste Option ist natürlich, die Ausnahme vollständig zu behandeln und die Fehlersituation in ihrer Gesamtheit zu behandeln. Wenn Sie dies können, tun Sie es auf jeden Fall! Dies kann sogar bedeuten, dass Sie die Ausnahme nicht protokollieren müssen.

Eine Möglichkeit, eine Ausnahme zu behandeln, besteht darin, eine andere Ausnahme auszulösen, die eine übergeordnete Beschreibung des Problems enthält (z. B. " failed to initialize" anstelle von " index out of bounds"). Dies ist ein gutes Muster, solange Sie nicht die Informationen über die Ursache der Ausnahme verlieren. Verwenden Sie die detaillierte Ausnahme, um die Ausnahme causeder übergeordneten Ausnahme zu initialisieren oder das Detail zu protokollieren (wie oben erläutert). Die Protokollierung ist am besten geeignet, wenn Sie eine prozessübergreifende Grenze überschreiten möchten, z. B. einen IPC-Aufruf, da nicht garantiert werden kann, dass die Ausnahmeklasse auf niedriger Ebene überhaupt am anderen Ende der Verbindung vorhanden ist. Das Festhalten als angehängte Ursache ist am besten geeignet, wenn eine innere Grenze überschritten wird.

Ein weiteres Muster, das Sie sehen, ist Catch-and-Release:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Dies ist ein Anti-Pattern, es sei denn, Sie haben Typeinschränkungen aus anderen catchKlauseln, die bedeuten, dass Sie die Ausnahme nicht einfach von alleine übergehen lassen können. Dann ist es nur eine hässliche Funktion von Java.

Es gibt keinen wirklichen Unterschied zwischen aktivierten und nicht aktivierten Ausnahmen außer der Tatsache, dass Sie aktivierte Ausnahmen deklarieren müssen , die Methodengrenzen überschreiten. Es ist immer noch eine gute Idee, ungeprüfte Ausnahmen (mit dem @throwsJavadoc-Kommentar) zu dokumentieren, wenn Sie wissen, dass sie absichtlich von Ihrem Code ausgelöst werden. Werfen Sie nicht absichtlich java.lang.Erroroder seine Unterklassen (es sei denn, Sie schreiben eine JVM-Implementierung).

Meinung: Ein unerwarteter Fehlerfall stellt immer einen Fehler in Ihrem Code dar. Überprüfte Ausnahmen sind eine Möglichkeit, diese Bedrohung zu verwalten. Wenn Entwickler absichtlich ungeprüfte Ausnahmen verwenden, um die Probleme bei der Behandlung von Fehlerfällen zu vermeiden, bauen Sie eine Menge technischer Schulden auf, die Sie einige Zeit bereinigen müssen Wenn Sie robusten Code wünschen. Eine schlampige Fehlerbehandlung ist unprofessionell (und die Fehlerbehandlung ist ein guter Weg, um festzustellen, wie gut ein Programmierer wirklich ist).

Donal Fellows
quelle
0

Ich denke, Sie sollten NUR eine aktivierte Ausnahme auslösen, wenn die Anwendung wiederhergestellt werden kann. Benutzer Ihrer Bibliothek müssen diese Ausnahmen abfangen und das tun, was sie zur Wiederherstellung benötigen. Alles andere sollte deaktiviert sein.

Als Beispiel:

Wenn Ihre App Daten entweder aus einer Datei oder einer Datenbank lädt. Dann,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

Der Aufrufer konnte immer die überprüfte MissingDataFileException abfangen und dann versuchen, die Daten aus der Datenbank zu laden.

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}
Hendrix
quelle
1
Saul starb vor 3 Jahren bei einem Autounfall. Scheitern! ;-)
Donal Fellows