Warum konsumieren Java-Benutzer häufig stillschweigend Ausnahmen?


81

Ich habe noch nie ernsthaft Java codiert, aber ich habe die Syntax, Bibliotheken und Konzepte basierend auf meinen vorhandenen Fähigkeiten (Delphi & C #) gelernt. Eine Sache, die ich kaum verstehe, ist, dass ich so viel Code gesehen habe, der stillschweigend Ausnahmen printStackTracewie folgt verbraucht :

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

In fast jedem Java-Artikel und -Projekt, auf das ich gestoßen bin, gibt es einen ähnlichen Code wie diesen. Nach meinem Wissen ist das sehr schlecht. Die Ausnahme sollte fast immer wie folgt an den äußeren Kontext weitergeleitet werden:

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

Meistens sollte die Ausnahme in der äußersten Schleife behandelt werden, die zum zugrunde liegenden Framework gehört (z. B. Java Swing). Warum sieht es in der Java-Welt wie die Norm aus, so zu codieren? Ich bin verwirrt.

Aufgrund meines Hintergrunds würde ich printStackTrace lieber vollständig entfernen . Ich würde es einfach als unbehandeltes aka RuntimeException(oder noch besser AssertionError) erneut werfen und es dann an der am besten geeigneten Stelle abfangen und protokollieren: der äußersten Framework-Schleife.

    public void process() {
        try {
            System.out.println("test");
        } catch(Exception e) {
            throw new AssertionError(e);
        }
    }

56
Ich würde sagen, dass es nicht wirklich still ist, wenn die Stapelverfolgung gedruckt wird :)
willcodejavaforfood

13
Darf ich Isaac Waller zitieren "Aber Ihr Programm wird nicht beendet und läuft weiter, höchstwahrscheinlich in einem undefinierten Zustand"
Sake

15
@willcodejavaforfood, Dein Kommentar hat so viele Stimmen bekommen und das verwirrt mich noch mehr über die typische Java-Denkweise.
Sake

13
@willcodejavaforfood - aus Sicht des Anrufers ist es still. Der Anrufer hat keine Ahnung, dass etwas passiert ist. Wenn das beabsichtigt ist, ist das in Ordnung, aber im Allgemeinen sind das schlechte Nachrichten. Denken Sie auch darüber nach, was in einem Applet passiert. Der Benutzer wird die Ausgabe nie sehen (es sei denn, er hat zufällig die Java-Konsole geöffnet, was niemals der Fall ist). Ok, also verwendet niemand mehr Applets;) Ähnlich für Servletm - Endbenutzer wissen nicht, dass etwas passiert ist - es wird nur in einem Protokoll gespeichert.
Scott Stanchfield

4
Das Auslösen von Ausnahmen in Java ist so weit verbreitet, dass die meisten Java-Codierer sie ignorieren, denn wenn Sie den richtigen Fehler abfangen, müssen Sie viel mehr Code schreiben ... also tun dies im Grunde alle diese Java-Programmierer, weil sie träge sind.
Trevor Boyd Smith

Antworten:


203

Ich habe immer gedacht, das ähnelt dem folgenden Szenario:

"Ein Mann wird erschossen.

Er hält den Atem an und hat genug Kraft, um einen Bus zu nehmen.

10 Meilen später steigt der Mann aus dem Bus, geht ein paar Blocks und stirbt. "

Wenn die Polizei an die Leiche kommt, hat sie keine Ahnung, was gerade passiert ist. Sie können schließlich haben, aber es ist viel schwieriger.

Besser ist:

"Ein Mann wird erschossen und stirbt sofort. Der Körper liegt genau dort, wo der Mord gerade passiert ist."

Wenn die Polizei eintrifft, sind alle Beweise vorhanden.

Wenn ein System ausfallen soll, ist es besser, schnell auszufallen

Beantwortung der Frage:

  1. Ignoranz.
      +
  2. Faultier

BEARBEITEN:

Natürlich ist der Fangabschnitt nützlich.

Wenn etwas mit der Ausnahme getan werden kann, sollte es dort getan werden.

Wahrscheinlich ist das KEINE Ausnahme für den angegebenen Code, wahrscheinlich wird dies erwartet (und in meiner Analogie ist es wie eine kugelsichere Jacke, und der Mann hat an erster Stelle auf den Schuss gewartet).

Und ja, der Fang könnte verwendet werden, um Ausnahmen zu werfen, die der Abstraktion entsprechen


2
Das Schlucken von Ausnahmen scheitert nicht, egal, schnell zu scheitern. Wenn Ihr Methodenvertrag hingegen einen Parameter enthält, der aus einer Ziffernfolge besteht, und Sie ihn in eine Ganzzahl analysieren, können Sie die ParseException verschlucken. Wenn Sie eine bestimmte Funktionalität schreiben, definieren Sie eine benutzerdefinierte Ausnahme für diese fehlgeschlagene Funktionalität, fangen Sie dann Ausnahmen ab und lösen Sie Ihre benutzerdefinierte Ausnahme aus (mit der ursprünglichen Ausnahme als Argument). Es kann dann schnell fehlschlagen, wo ein sinnvolles Verhalten auftreten kann (Anzeige für Benutzer, E-Mail senden, einfach protokollieren, diesen Server-Thread beenden usw.).
JeeBee

21
Moment mal ... Ich muss zuerst aufhören zu lachen ... Okay ... Das war bei weitem die unterhaltsamste Antwort auf eine Frage, die ich bisher gesehen habe. Es bringt sehr viel auf den Punkt. Obwohl es sehr hilfreich sein kann, die Ausnahme für die Behandlung "up the chain" zu übergeben, ist es besser, zuerst die Ausnahme abzufangen und sie dann zu übergeben, damit einige nützliche Informationen enthalten sein können.
Herr Will

Die Ausnahme ist nicht der Fehler. Eine Ausnahme wird gemacht, wenn der Mann erschossen wird. Anschließend wird der Vorfall in einer Notiz aufgeführt und an eine Brieftaube angehängt, die darauf trainiert ist, zum nächsten Mordkommando zu fliegen.
Jherico

13
@ Oscar, +100 wenn ich könnte. Eine schlechte Ausnahmebehandlung ist das Ergebnis von Ignoranz und MALICE, nicht von Faulheit. Faulheit = so wenig Aufwand wie möglich. Dummheit im Umgang mit Ausnahmen verursacht mehr Arbeit, reduziert sie nicht ... natürlich verursacht sie normalerweise mehr Arbeit für jemand anderen, weshalb es "faul" erscheint.
James Schek

1
Ich sehe Ihren Punkt Oscar, aber alles, was die Ausnahme behandelt oder protokolliert, zeigt Ihnen, dass die Ausnahme aufgetreten ist. Wenn Sie es ganz in die Kette zurückwerfen, erfahren Sie, welche Methode letztendlich die Ausnahme ausgelöst hat.
Mauro

33

Normalerweise liegt dies daran, dass die IDE eine hilfreiche 'Schnellkorrektur' anbietet, die den fehlerhaften Code mit dieser Ausnahmebehandlung in einen Try-Catch-Block einschließt. Die Idee ist, dass Sie tatsächlich etwas tun, faule Entwickler jedoch nicht.

Das ist zweifellos eine schlechte Form.


2
Einverstanden sieht das angegebene Beispiel wie die Standard-Schnellkorrektur von Eclipse aus, wobei der Kommentar // FIXME: entfernt wurde.
JeeBee

2
Nun, Sie sagten "Das ist zweifellos eine schlechte Form " und ich stimme zu 100% zu. Einige andere Antworten und Kommentare machen mich jedoch sehr neugierig auf die Philosophie hinter diesem sehr großen Teil (glaube ich) von Java-Entwicklern.
Sake

4
Ich wünschte wirklich, Eclipse würde dort etwas drastisches tun. Wie e.printStackTrace (); System.exit (1);
Adam Jaskiewicz

4
Ich habe meine IDE-Einstellungen geändert, um einen Catch-Body zu generieren, der ihn als RuntimeException erneut ausgibt. Das ist eine viel bessere Standardeinstellung.
Esko Luontola

6
Wenn ich sicher bin, dass die Ausnahme niemals auftreten sollte, löse ich normalerweise einen AssertionError anstelle von RuntimeException, um meine Absicht zu dokumentieren.
Esko Luontola

20

Dies ist ein klassisches Strohmann-Argument . printStackTrace()ist eine Debugging-Hilfe. Wenn Sie es in einem Blog oder in einer Zeitschrift gesehen haben, war dies darauf zurückzuführen, dass der Autor mehr daran interessiert war, einen anderen Punkt als die Ausnahmebehandlung zu veranschaulichen . Wenn Sie es im Produktionscode gesehen haben, war der Entwickler dieses Codes unwissend oder faul, sonst nichts. Es sollte nicht als Beispiel für die gängige Praxis in der "Java-Welt" angeführt werden.


1
Also ist es für Pro-Programmierer in Ordnung, schlechten Code in Blog oder Magazin zu schreiben und nicht in der Produktion? Ich schreibe lieber schlechten Code in der Produktion. Wenn ein wirklich kluger Kerl printStackTrace verwendet, überzeugt er die Leute davon. Ich verstehe, dass der Ausnahmecode 8 Zeichen länger ist, aber ...
IAdapter

8
@ ABCDE: Ich bin völlig anderer Meinung. Sie möchten lieber schlechten Code in der Produktion schreiben? Das ist Code, der funktionieren soll! Blog- und Zeitschriftenartikel sollen einen Punkt veranschaulichen. Wenn Fehlerbehandlung nicht der Punkt ist, kann es nur Unordnung sein. Viele Autoren verwenden eine stark vereinfachte oder gar keine Fehlerbehandlung in Artikeln. Dies ist kein Beispiel, dem man folgen sollte.
Bill the Lizard

19
  1. Java zwingt Sie, alle Ausnahmen explizit zu behandeln. Wenn eine Methode , dass Ihr Code Anrufe deklariert FooException und BarException Ihren Code zu werfen MUST Griff (oder werfen) , um diese Ausnahmen. Die einzige Ausnahme ist RuntimeException , die wie ein Ninja schweigt.
  2. Viele Programmierer sind faul (ich selbst eingeschlossen), und es ist sehr einfach, nur den Stack-Trace zu drucken.

7
Ihr Programm wird jedoch nicht beendet und läuft weiter, höchstwahrscheinlich in einem undefinierten Zustand.
Isaac Waller

5
Wie ist der Status undefiniert, wenn Sie die Art der Ausnahme kennen? Das ist ein süßes kleines Zitat, aber normalerweise KANN und MUSS das Programm weitermachen. Sie möchten nicht, dass die gesamte Anwendung für eine Ausnahme abstürzt. Teilen Sie dies dem Benutzer mit und bitten Sie ihn, das zu tun, was es erneut war.
GreenieMeanie

2
Die Anwendung stürzt nicht ab, wenn die Ausnahme auf der äußersten Ebene ordnungsgemäß behandelt wird.
Sake

2
Mindestens die Hälfte der Ausnahmen sind "RuntimeExceptions". Sie lassen es so klingen, als gäbe es nur eine. Und es ist nicht still und RutimeException druckt eine Stapelverfolgung und stürzt Ihr Programm ab.
Bill K

@greenieMeanie Im Allgemeinen möchten Sie bei der Entwicklung, dass es mit dem geringsten Problem zum Absturz kommt - lassen Sie Entwickler nicht davonkommen, einen Anruf zu vermasseln - verzeihen Sie nichts. Auf der anderen Seite, wenn Sie es versenden, möchten Sie das Gegenteil tun. Protokollieren Sie jede Ausnahme unsichtbar und versuchen Sie nach besten Kräften, sie wiederherzustellen und fortzusetzen (was Java wirklich recht gut kann).
Bill K

12

Ich finde, es gibt oft zwei Gründe, warum dies getan wird

  1. Der Programmierer war faul
  2. Der Programmierer wollte einen Einstiegspunkt in die Komponente schützen (richtig oder falsch).

Ich glaube nicht, dass dies ein Phänomen ist, das auf Java beschränkt ist. Ich habe solche Codierungen auch oft in C # und VB.Net gesehen.

An der Oberfläche ist es ziemlich schockierend und sieht schrecklich aus. Aber es ist wirklich nichts Neues. Es tritt ständig in C ++ - Anwendungen auf, die Fehlercode-Rückgabewerte im Vergleich zu Ausnahmen verwenden. Der Unterschied besteht jedoch darin, dass das Ignorieren eines möglicherweise schwerwiegenden Rückgabewerts nicht wirklich anders aussieht als das Aufrufen einer Funktion, die void zurückgibt.

Foo* pFoo = ...;
pFoo->SomeMethod(); // Void or swallowing errors, who knows?

Dieser Code sieht besser aus, aber wenn SomeMethod () ein HResult zurückgeben würde, wäre dies semantisch nicht anders als das Verschlucken einer Ausnahme.


3
Ich denke, Grund 1 ist weitaus häufiger als Grund 2 für diese Frage
matt b

2
@matt b, stimmte zu. Faulheit ist für einen Großteil der Fehler verantwortlich, die ich heutzutage behebe.
JaredPar

Das Ignorieren eines Fehlercodes ist ein verdammt schnellerer Anblick und verwendet viel weniger Boilerplate-Code!
Gbjbaanb

11

weil Checked Exceptions ein fehlgeschlagenes Experiment ist

(Vielleicht ist printStackTrace () das eigentliche Problem ? :)


17
Sie sind kein fehlgeschlagenes Experiment. Eigentlich mag ich sie! Wie in anderen Antworten hervorgehoben, neigen Programmierer dazu, faul zu sein und Fehlerfälle einfach zu ignorieren. Da geprüfte Ausnahmen Sie zwingen, mit ihnen umzugehen, haben Sie insgesamt einen besseren Code. (Wenn der Programmierer sie nicht stillschweigend ignoriert). Auch wenn Sie nicht faul sind, können Sie nicht vergessen, eine wichtige Ausnahme
Malax

5
Geprüfte Ausnahmen sind aus mehreren Gründen ein Fehler: Überprüfen Sie einige von ihnen mindview.net/Etc/Discussions/CheckedExceptions
dfa

2
+1 konnte nicht mehr zustimmen. Überprüfte Ausnahmen machen Code oft weniger sicher und dies ist ein Beispiel dafür, wie.
Cletus

2
Überprüfte Ausnahmen sind definitiv nicht perfekt, aber sie zwingen Sie, sich der Realität zu stellen, dass die Dinge manchmal nicht nach Plan verlaufen. Nicht überprüfte Ausnahmen versuchen nur vorzutäuschen, dass wir in einer perfekten Welt leben, in der nichts schief geht
mcjabberz

2
@Peter Lawrey: Überprüfte Ausnahmen sind ein Fehler, weil sehr kluge Leute wie diejenigen, die mit dem erstaunlichen Swing-Framework gekommen sind, auf sie kotzen und sich weigern, sie zu verwenden. Und ich bezweifle sehr, dass Leute wie Joshua Bloch oder diejenigen, die hinter dem Spring-Framework stehen, "nicht wissen, wie man sie richtig benutzt". Sicher, Sie können mit einer aktivierten Ausnahme richtig umgehen. Die wirkliche Zerbrochenheit ist, dass jemand irgendwo dachte, dass das Werfen eine akzeptable Praxis sei.
SyntaxT3rr0r

6

Ich muss sagen, dass ich den Ton, der diese Art von laxem Fehlerbehandlungsverhalten impliziert, etwas ablehne, was für Java-Programmierer von grundlegender Bedeutung ist. Sicher, Java-Programmierer können wie jeder andere Programmierer faul sein, und Java ist eine beliebte Sprache, sodass Sie wahrscheinlich viele Ausnahmen beim Verschlucken von Code sehen werden.

Wie bereits an anderer Stelle erwähnt, gibt es verständliche Frustrationen bei der erzwungenen Deklaration geprüfter Ausnahmen durch Java, obwohl ich persönlich damit kein Problem habe.

Ich denke, ich habe ein Problem damit, dass Sie eine Reihe von Artikeln und Codefragmenten im Web durchblättern, ohne sich die Mühe zu machen, den Kontext zu berücksichtigen. Die Wahrheit ist, wenn Sie einen technischen Artikel schreiben, in dem erklärt wird, wie eine bestimmte API funktioniert oder wie Sie mit etwas beginnen, überspringen Sie sehr wahrscheinlich einige Aspekte des Codes - die Fehlerbehandlung, die nicht direkt erfolgt In Bezug auf das, was Sie demonstrieren, ist ein wahrscheinlicher Kandidat für die Entsorgung, insbesondere wenn es unwahrscheinlich ist, dass die Ausnahme im Beispielszenario auftritt.

Leute, die Artikel dieser Art schreiben, müssen ein angemessenes Signal-Rausch-Verhältnis einhalten, und ich denke, das bedeutet ziemlich fair, dass sie davon ausgehen müssen, dass Sie einige Grundlagen über die Sprache kennen, in der Sie sich entwickeln. wie man richtig mit Fehlern umgeht und eine Menge anderer Dinge. Wenn Sie auf einen Artikel stoßen und feststellen, dass keine ordnungsgemäße Fehlerprüfung durchgeführt wurde, ist dies in Ordnung. Stellen Sie nur sicher, dass Sie, wenn Sie diese Ideen (aber natürlich nie den genauen Code selbst, oder?) in Ihren Produktionscode integrieren, mit all den Kleinigkeiten umgehen, die der Autor vernünftigerweise ausgelassen hat, auf eine Weise, die am meisten ist passend zu dem, was Sie entwickeln.

Ich habe ein Problem mit sehr einführenden Artikeln auf hoher Ebene, die über solche Probleme hinweggehen, ohne jemals darauf zurückzukommen, aber bitte beachten Sie, dass Java-Programmierer keine bestimmte "Denkweise" in Bezug auf die Fehlerbehandlung haben. Ich kenne viele Ihrer geliebten C # -Programmierer, die sich auch nicht mit all ihren Problemen befassen.


Aber das Schreiben eines hochrangigen Artikels mit try {...} catch (IOException e) {}ist einfach dumm (und ein TODO hilft NICHT). Es wird sicherlich viele Anfänger irreführen, dasselbe zu tun. Außerdem ist es viel mehr zu schreiben als throws IOException, was die meiste Zeit richtig ist. Vielleicht ist es einmal pro Papier nicht möglich und dann ist das Schreiben throw wrap(e)(ohne langweilig zu definieren wrap) eine gute Lösung. Eines der ersten Dinge, die ich in Java gelernt habe, ist, dass stilles Schlucken eine schreckliche Sache ist und ich sehr vorsichtig bin, damit ich es nicht einmal vorübergehend mache (selbst Abstürze sind auf lange Sicht viel besser).
Maaartinus

5

Ein System.out-Druck oder e.printStackTrace () - was die Verwendung von System.out impliziert, ist normalerweise eine rote Fahne, was bedeutet, dass sich jemand nicht die Mühe gemacht hat, einen sorgfältigen Job zu erledigen. Mit Ausnahme von Desktop-Java-Anwendungen sind die meisten Java-Apps besser für die Protokollierung geeignet.

Wenn der Fehlermodus für eine Methode keine Operation ist, ist es vollkommen in Ordnung, eine Ausnahme zu essen, unabhängig davon, ob Sie den Grund (und die Existenz) aufzeichnen oder nicht. Typischerweise sollte die catch-Klausel jedoch außergewöhnliche Maßnahmen ergreifen.

Das erneute Auslösen einer Ausnahme ist am besten geeignet, wenn Sie entweder den Catch verwenden, um einen Teil der Arbeit auf einer Ebene zu bereinigen, auf der die erforderlichen Informationen noch verfügbar sind, oder wenn Sie die Ausnahme in einen Ausnahmetyp umwandeln müssen, der für den Anrufer zugänglicher ist.


3

Es wird nur still verbraucht, wenn der Fangblock wirklich leer ist.

Was Artikel betrifft, sind sie wahrscheinlich interessanter, um einen anderen Punkt zu beweisen, als mit Ausnahmen umzugehen. Sie wollen nur direkt auf den Punkt kommen und den kürzestmöglichen Code haben.

Natürlich haben Sie Recht, Ausnahmen sollten zumindest protokolliert werden, wenn sie "ignoriert" werden sollen.


3

Wie andere bereits betont haben, ist dies einer von drei Gründen:

  1. Eine IDE hat den Try-Catch-Block generiert
    • Der Code wurde kopiert und eingefügt
    • Der Entwickler hat den Stacktrace zum Debuggen eingefügt, ist jedoch nie zurückgekommen, um die Ausnahme ordnungsgemäß zu behandeln

Der letzte Punkt tritt am seltensten auf. Ich sage das, weil ich nicht glaube, dass jemand auf diese Weise wirklich debuggt. Das Durchlaufen von Code mit einem Debugger ist eine viel einfachere Möglichkeit zum Debuggen.

Die beste Beschreibung dessen , was in einem Catch-Block getan werden sollte , finden Sie in Kapitel 9 von Effective Java von Joshua Bloch .


2

In C # sind alle Ausnahmen Laufzeitausnahmen, in Java gibt es jedoch Laufzeitausnahmen und aktivierte Ausnahmen, die Sie entweder abfangen oder in Ihren Methoden deklarieren müssen. Wenn Sie eine Methode aufrufen, die am Ende einen "Wurf" hat, müssen Sie entweder die dort genannten Ausnahmen abfangen oder Ihre Methode muss auch diese Ausnahmen deklarieren.

Java-Artikel drucken normalerweise nur die Stapelverfolgung oder haben einen Kommentar, da die Ausnahmebehandlung für den Artikelgegenstand irrelevant ist. In Projekten sollte jedoch je nach Art der Ausnahme etwas dagegen unternommen werden.


2

Sie sollten dies sehr oft sehen, wenn der Programmierer seine Arbeit richtig macht. Ausnahmen zu ignorieren ist eine schlechte, schlechte Praxis! Es gibt jedoch einige Gründe, warum einige dies und die angemesseneren Lösungen tun könnten:

  • "Das wird nicht passieren!" Sicher, manchmal "wissen" Sie, dass diese Ausnahme nicht auftritt, aber es ist noch angemessener, eine Laufzeitausnahme mit der aufgetretenen Ausnahme als "Ursache" erneut auszulösen, anstatt sie einfach zu ignorieren. Ich wette, es wird irgendwann in der Zukunft passieren. ;-);

  • Prototyping-Code Wenn Sie nur Ihre Daten eingeben, um festzustellen, ob sie funktionieren, möchten Sie möglicherweise alle möglicherweise auftretenden Ausnahmen ignorieren. Dies ist der einzige Fall, in dem ich faul fange (Throwable). Aber wenn sich der Code als nützlich herausstellt, schließe ich die richtige Ausnahmebehandlung ein.

  • "Ich weiß nicht was ich tun soll!" Ich habe viel Code gesehen, insbesondere Bibliothekscode, der auftretende Ausnahmen verschluckt, da in dieser Ebene der Anwendung keine ordnungsgemäße Behandlung durchgeführt werden kann. TUN SIE DAS NICHT! Wirf die Ausnahme einfach erneut aus (entweder durch Hinzufügen einer Throws-Klausel in der Methodensignatur oder durch Umschließen der Ausnahme in eine bibliotheksspezifische).


3
Als ich den Fang (Throwable) sah, zuckte ich zusammen.
Bill the Lizard

2
Absolut. Ich benutze einfach entweder catch (Throwable) oder Throwable in Code, wenn ich herumspiele. "Echter" Code darf ihn nicht enthalten! Zeitraum.
Malax

Einverstanden. Ich zucke zusammen, weil ich es schon einmal gesehen habe. Wenn ein anderer Entwickler sieht, dass Sie catch (Throwable) geschrieben haben, ist es zu weit gegangen! :)
Bill the Lizard

1
Sie möchten (Throwable) auf der obersten Ebene fangen, selbst wenn der Code den Prozess beendet. Der Versuch, mit einem fehlenden Faden zu humpeln, ist möglicherweise nicht die beste Idee.
Tom Hawtin - Tackline

2

Sie sollten es immer weiterleiten oder in einem realen Kontext angemessen handhaben. Viele Artikel und Tutorials vereinfachen ihren Code, um die wichtigeren Punkte zu vermitteln, und eines der am einfachsten zu vereinfachenden Dinge ist die Fehlerbehandlung (es sei denn, Sie schreiben einen Artikel über die Fehlerbehandlung :)). Da Java-Code die Ausnahmebehandlung überprüft, ist das Setzen eines einfachen stillen (oder Protokollierungsanweisungs-) Catch-Blocks die einfachste Methode, um ein funktionierendes Beispiel bereitzustellen.

Wenn Sie dies in etwas anderem als Beispielcode finden, können Sie den Code gerne an TDWTF weiterleiten, obwohl es möglicherweise zu viele Beispiele dafür gibt :)


2

Ich befürchte, dass die meisten Java-Programmierer nicht wissen, was sie mit Ausnahmen tun sollen, und betrachten dies ziemlich immer als Ärger, der ihre Codierung des "nominalen" Falls verlangsamt. Natürlich liegen sie völlig falsch, aber es ist schwierig, sie davon zu überzeugen, dass es wichtig ist, mit Ausnahmen richtig umzugehen. Jedes Mal, wenn ich einem solchen Programmierer begegne (es kommt häufig vor), gebe ich ihm zwei Leseeinträge:

  • das berühmte Denken in Java
  • Ein kurzer und interessanter Artikel von Barry Ruzek ist hier verfügbar: www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html

Übrigens stimme ich voll und ganz zu, dass es dumm ist, eine typisierte Ausnahme abzufangen, um sie in eine RuntimeException eingebettet erneut auszulösen:

  • Wenn Sie es fangen, behandeln Sie es.
  • Andernfalls ändern Sie Ihre Methodensignatur, um mögliche Ausnahmen hinzuzufügen, die Sie behandeln würden / könnten, sodass Ihr Anrufer die Möglichkeit hätte, dies selbst zu tun.

Ich würde Bruce Eckel in dieser Angelegenheit zu 100% nicht vertrauen :)
CurtainDog

2

Die Kombination von geprüften Ausnahmen und Schnittstellen führt dazu, dass der Code Ausnahmen verarbeiten muss, die niemals ausgelöst werden. (Gleiches gilt auch für die normale Vererbung, ist jedoch häufiger und einfacher mit Schnittstellen zu erklären.)

Grund: Die Implementierung einer Schnittstelle darf keine anderen als die in der Schnittstellenspezifikation definierten (aktivierten) Ausnahmen auslösen. Aus diesem Grund geben die Ersteller einer Schnittstelle, die nicht wissen, welche Methoden einer Klasse, die die Schnittstelle implementiert, möglicherweise tatsächlich eine Ausnahme auslösen müssen, möglicherweise an, dass alle Methoden mindestens einen Ausnahmetyp auslösen. Beispiel: JDBC, wo alles und seine Oma deklariert werden, um SQLException auszulösen.

In der Realität können viele Methoden realer Implementierungen einfach nicht fehlschlagen, sodass sie unter keinen Umständen jemals eine Ausnahme auslösen. Code, der diese Methoden aufruft, muss die Ausnahme immer noch irgendwie "behandeln", und der einfachste Weg ist, die Ausnahme zu verschlucken. Niemand möchte seinen Code mit scheinbar nutzloser Fehlerbehandlung überladen, die niemals ausgeführt wird.


1
IMHO sollte es eine Sprachkürzel geben, um Ausnahmen wie Laufzeitfehler abzufangen und erneut auszulösen. Das Notieren einer solchen Abkürzung würde sowohl im Code als auch im Verhalten klar machen, dass solche Ausnahmen als "unerwartete" Bedingungen angesehen werden sollten. Das Schlucken ist schlecht, denn wenn eine Ausnahme ausgelöst wird, stimmt etwas nicht und der aufrufende Code sollte es herausfinden.
Supercat

supercat: Ich mag diese Idee, aber sie könnte den (IMO fragwürdigen) Vorteil von geprüften Ausnahmen aufheben.
Erich Kitzmüller

Es wäre besser, wenn die Überprüfbarkeit eher ein Merkmal von Fang- / Wurfstellen und Ausnahmeinstanzen als von Ausnahmeklassen wäre, so dass sich geprüfte Ausnahmen als ungeprüfte Ausnahmen derselben Klasse verbreiten könnten, aber selbst Wrap-and-Rethrow wäre eine wesentliche Verbesserung der Status Quo, und es könnte leicht als neue Funktion hinzugefügt werden. Dies ist besonders nützlich, wenn eine Methode, die möglicherweise eine aktivierte Ausnahme auslöst, andere Methoden aufruft, die als dieselbe Ausnahme deklariert deklariert werden, von denen jedoch nicht erwartet wird, dass sie dies jemals tatsächlich tun . Wenn eine der letzteren Methoden wirft ...
Supercat

... ein Aufrufer, der erwartet, die von der äußeren Methode ausgelöste Ausnahme zu behandeln, sollte keine von den inneren Methoden ausgelöste Ausnahme abfangen, da dies nicht die vom Aufrufer erwartete Bedingung darstellt. Wenn ich ein Laufzeitframework von Grund auf neu entwerfe, würde ich einen anderen Mechanismus für aktivierte und nicht aktivierte Ausnahmen verwenden, sodass aktivierte Ausnahmen einen erfolgreichen Overhead für einen erfolgreichen Funktionsaufruf bedeuten, beim Auslösen jedoch viel weniger Overhead verursachen als nicht aktivierte Ausnahmen (unter anderem) , eine Ausnahme, die als geprüft abgefangen wurde, hätte keine Stapelverfolgung).
Supercat

1

Wie bereits erwähnt, ist das Aufrufen von printStackTrace () keine wirklich stille Behandlung.

Der Grund für diese Art des "Verschluckens" der Ausnahme ist, dass Sie die Ausnahme immer noch irgendwo behandeln oder die Anwendung abstürzen lassen müssen, wenn Sie die Ausnahme in der Kette weitergeben . Die Behandlung auf der Ebene, auf der sie mit einem Informationsspeicherauszug auftritt, ist also nicht schlechter als die Behandlung auf der obersten Ebene mit einem Informationsspeicherauszug.


1

Es ist faul zu üben - nichts weniger als es wirklich.

Dies geschieht normalerweise, wenn Sie sich wirklich nicht um die Ausnahme kümmern - anstatt Ihre Fingerarbeit zu erhöhen.


1

Ich bin nicht der Meinung, dass das erneute Auslösen einer aktivierten Ausnahme eine bessere Idee ist. Fangen bedeutet Handhaben; Wenn Sie neu werfen müssen, sollten Sie nicht fangen. In diesem Fall würde ich die Throws-Klausel zur Methodensignatur hinzufügen.

Ich würde sagen, dass das Umschließen einer geprüften Ausnahme in eine ungeprüfte Ausnahme (z. B. die Art und Weise, wie Spring die geprüfte SQLException in eine Instanz ihrer ungeprüften Hierarchie umschließt) akzeptabel ist.

Die Protokollierung kann als Behandlung betrachtet werden. Wenn das Beispiel geändert würde, um den Stack-Trace mit log4j zu protokollieren, anstatt in die Konsole zu schreiben, würde dies dies akzeptabel machen? Keine große Veränderung, IMO.

Das eigentliche Problem ist das, was als außergewöhnlich und als akzeptables Wiederherstellungsverfahren angesehen wird. Wenn Sie die Ausnahme nicht beheben können, können Sie den Fehler am besten melden.


1

Bitte verpacken Sie niemals eine aktivierte Ausnahme in eine nicht aktivierte Ausnahme.

Wenn Sie mit Ausnahmen zu tun haben, von denen Sie nicht glauben, dass Sie sie sollten, dann ist mein Rat, dass Sie wahrscheinlich auf der falschen Abstraktionsebene arbeiten.

Ich werde näher darauf eingehen: Geprüfte und ungeprüfte Ausnahmen sind zwei sehr unterschiedliche Bestien. Überprüfte Ausnahmen ähneln der alten Methode zur Rückgabe von Fehlercodes ... Ja, sie sind etwas schmerzhafter zu handhaben als Fehlercodes, haben aber auch Vorteile. Nicht aktivierte Ausnahmen sind Programmierfehler und kritische Systemfehler ... also außergewöhnliche Ausnahmen. Beim Versuch zu erklären, was eine Ausnahme ist, geraten viele Menschen in ein völliges Chaos, weil sie den Unterschied in diesen beiden, sehr unterschiedlichen Fällen nicht anerkennen.


Das Einwickeln in AssertionError ist für mich vernünftig. AssertionError ist die Abstraktion, die überall angewendet wird, IMO.
Sake

Ich werde näher darauf eingehen: Geprüfte und ungeprüfte Ausnahmen sind zwei sehr unterschiedliche Bestien. Überprüfte Ausnahmen ähneln der alten Methode zur Rückgabe von Fehlercodes ... ja, sie sind etwas schmerzhafter zu handhaben als Fehlercodes, haben aber auch Vorteile. Nicht aktivierte Ausnahmen sind Programmierfehler und kritische Systemfehler ... also außergewöhnliche Ausnahmen. Beim Versuch zu erklären, was eine Ausnahme ist, geraten viele Menschen in ein völliges Chaos, weil sie den Unterschied in diesen beiden, sehr unterschiedlichen Fällen nicht anerkennen.
CurtainDog

Wahrscheinlich bin ich unter den Leuten, die, wie Sie sagen, in ein komplettes Chaos geraten. Mein eigentlicher Punkt ist jedoch, warum die Ausführung fortgesetzt werden darf, während Sie die Wahl haben, sie zu unterbrechen. (entweder mit AssertionError oder, wenn Sie es vorziehen, ändern Sie Ihre Schnittstelle, um zusätzliche Ausnahme einzuschließen.
Sake

1
Konnte der primären Behauptung dieser Antwort nicht mehr widersprechen. Die Ausarbeitung hat Vorzüge.
Lawrence Dol

@CurtainDog: Überprüfte Ausnahmen gelten für "erwartete" Probleme. Wenn Methode x wirft (markiert) FooException in einigen Fällen und Verfahren y, die x ruft, erwartet nicht , solche Fälle auftreten, dann , wenn solche Fälle haben ergeben, sie ein darstellen unerwartetes Problem; Selbst wenn sich etwas weiter oben im Aufrufstapel befindet, das a erwartet FooException, stimmt die aktuelle Situation nicht mit dem überein, was dieser Upstream-Anrufer erwarten würde, wenn a FooExceptiongeworfen wird.
Supercat

1

Weil sie diesen Trick noch nicht gelernt haben:

class ExceptionUtils {
    public static RuntimeException cloak(Throwable t) {
        return ExceptionUtils.<RuntimeException>castAndRethrow(t);
    }

    @SuppressWarnings("unchecked")
    private static <X extends Throwable> X castAndRethrow(Throwable t) throws X {
        throw (X) t;
    }
}

class Main {
    public static void main(String[] args) { // Note no "throws" declaration
        try {
            // Do stuff that can throw IOException
        } catch (IOException ex) {
            // Pretend to throw RuntimeException, but really rethrowing the IOException
            throw ExceptionUtils.cloak(ex);
        }
    }
}

Das ist ein netter Trick, aber ich persönlich würde es vorziehen, eine spezifischere Ausnahme mit einer Ursache wie "Neue ServiceUnavailableException (ex) werfen" auszulösen
Greg

Die Rethrow-Methode von Apaches ExceptionUtils macht genau das. Es ist ein guter Trick. github.com/apache/commons-lang/blob/master/src/main/java/org/…
Lluis Martinez

1

Der eigentliche Punkt der Ausnahmen besteht darin, die Fehlerbehandlung zu vereinfachen und von der Fehlererkennung zu trennen. Dies steht im Gegensatz zur Darstellung von Fehlern durch Fehlercodes, bei denen der Fehlerbehandlungscode überall verstreut ist und jeder möglicherweise fehlgeschlagene Anruf auf Rückkehrcode überprüft werden muss.

Wenn eine Ausnahme einen Fehler darstellt (was in den meisten Fällen der Fall ist), ist es normalerweise am sinnvollsten, ihn zu behandeln, indem Sie ihn retten und die Behandlung einer oberen Schicht überlassen. Das erneute Auslösen einer anderen Ausnahme sollte in Betracht gezogen werden, wenn eine sinnvolle Semantik hinzugefügt wird, dh dieser Fehler ist ein ungewöhnlicher Systemfehler / ein vorübergehendes (Netzwerk-) Problem / dies ist ein clientseitiger oder serverseitiger Fehler usw.

Von allen Fehlerbehandlungsstrategien ist es am unwissendsten, Fehlermeldungen zu verbergen oder einfach auszudrucken und fortzufahren, da nichts passiert ist.


Sun-Leute wollten, dass der Code expliziter ist, und zwangen Programmierer, zu schreiben, welche Ausnahmen mit welcher Methode ausgelöst werden können. Es schien ein richtiger Schritt zu sein - jeder wird wissen, was er von einem Methodenaufruf erwarten kann, wenn er einen Prototyp hat (er kann einen Wert dieses Typs zurückgeben oder eine Instanz einer der angegebenen Klassen (oder seiner Unterklasse) auslösen).

Aber wie sich bei vielen ignoranten Java-Programmierern herausstellte, behandeln sie die Ausnahmebehandlung jetzt so, als wäre es ein Sprachfehler / eine "Funktion", die eine Problemumgehung benötigt und Code auf die schlimmste oder fast schlimmste Art und Weise schreibt:

  • Der Fehler wird sofort in einem Kontext behandelt, der nicht geeignet ist, um zu entscheiden, was damit zu tun ist.
  • Es wird stillschweigend angezeigt oder ignoriert, und die Berechnung wird fortgesetzt, auch wenn weiterer Code keine Chance hat, ordnungsgemäß ausgeführt zu werden.
  • Der Aufrufer der Methode kann nicht unterscheiden, ob sie erfolgreich beendet wurde oder nicht.

Wie schreibe ich den "richtigen Weg" als?

  • Geben Sie jede Basisklasse von Ausnahmen an, die im Methodenheader ausgelöst werden können. AFAICR Eclipse kann dies automatisch tun.
  • Machen Sie die Wurfliste im Methodenprototyp aussagekräftig. Lange Listen sind sinnlos und "Ausnahme auslösen" ist faul (aber nützlich, wenn Sie sich nicht viel um Ausnahmen kümmern).
  • Wenn Sie den "falschen Weg" schreiben, ist "throw Exception" viel besser und benötigt weniger Bytes als "try {...} catch (Exception e) {e.printStackTrace ();}".
  • Verkettete Ausnahme bei Bedarf erneut auslösen.

0

Wenn Sie möchten, dass Ihre Ausnahme außerhalb des Bereichs der aktuellen Methode behandelt wird, müssen Sie sie nicht tatsächlich abfangen, sondern fügen der Methodensignatur die Anweisung 'throw' hinzu.

Die try / catch-Anweisungen, die Sie gesehen haben, befinden sich nur in dem Code, in dem der Programmierer ausdrücklich beschlossen hat, die Ausnahme an Ort und Stelle zu behandeln und sie daher nicht weiter zu werfen.


1
Nicht wahr - Wenn Sie eine Schnittstellenmethode implementieren oder eine Superklassenmethode überschreiben, die keine 'Throws'-Deklaration enthält, können Sie nicht einfach eine hinzufügen. In diesen Fällen müssen Sie die Ausnahme entweder in eine RuntimeException einschließen oder verschlucken.
Martin McNulty

0

Ich denke, Entwickler versuchen auch zu berücksichtigen, wie wichtig es ist, in einem bestimmten Kontext "das Richtige zu tun". Oftmals, wenn Sie Code wegwerfen oder die Ausnahme nach oben verbreiten, wird nichts gekauft, da die Ausnahme schwerwiegend ist. Sie können auch Zeit und Mühe sparen, indem Sie "das Falsche tun".


0

Normalerweise schlucken Sie eine Ausnahme, wenn Sie sich nicht davon erholen können, dies jedoch nicht kritisch ist. Ein gutes Beispiel ist die IOExcetion, die beim Schließen einer Datenbankverbindung ausgelöst werden kann. Möchten Sie wirklich abstürzen, wenn dies passiert? Deshalb haben Jakartas DBUtils closeSilently-Methoden.

Schlucken Sie nun die aktivierte Ausnahme, die Sie nicht wiederherstellen können, die jedoch kritisch ist (normalerweise aufgrund von Programmierfehlern), nicht. Ich denke, Ausnahmen sollten so nahe wie möglich an der Ursache des Problems protokolliert werden, daher würde ich nicht empfehlen, den Aufruf von printStackTrace () zu entfernen. Sie möchten sie in RuntimeException umwandeln, um diese Ausnahmen nicht in Ihrer Geschäftsmethode deklarieren zu müssen. Es ist wirklich nicht sinnvoll, übergeordnete Geschäftsmethoden wie createClientAccount () zu haben, die ProgrammingErrorException auslösen (SQLException lesen). Sie können nichts tun, wenn Sie Tippfehler in Ihrer SQL haben oder auf fehlerhafte Indizes zugreifen.


0

Erfahrungsgemäß ist das Verschlucken einer Ausnahme meistens dann schädlich, wenn sie nicht gedruckt wird. Es hilft, die Aufmerksamkeit auf sich zu ziehen, wenn Sie abstürzen, und ich werde das manchmal absichtlich tun, aber wenn Sie einfach die Ausnahme drucken und fortfahren, können Sie das Problem finden und beheben, und andere, die an derselben Codebasis arbeiten, wirken sich normalerweise nicht negativ aus .

Ich mag Fail-Fast / Fail-HARD, aber drucke es zumindest aus. Wenn Sie tatsächlich eine Ausnahme "essen" (was wirklich nichts bedeutet: {}), kostet es jemanden TAGE, sie zu finden.

Das Problem ist, dass Java Sie zwingt, eine Menge Dinge zu fangen, von denen der Entwickler weiß, dass sie nicht geworfen werden, oder es ist ihm egal, ob sie es sind. Am häufigsten ist Thread.sleep (). Mir ist klar, dass es hier Löcher gibt, durch die Threading-Probleme auftreten können, aber im Allgemeinen wissen Sie, dass Sie es nicht unterbrechen. Zeitraum.


0

Es kann viele Gründe geben, warum man catch Exception verwenden würde. In vielen Fällen ist es eine schlechte Idee, weil Sie auch RuntimeExceptions abfangen - und Sie wissen nicht, in welchem ​​Zustand sich die zugrunde liegenden Objekte danach befinden werden? Bei unerwarteten Bedingungen ist das immer schwierig: Können Sie darauf vertrauen, dass der Rest des Codes danach nicht fehlschlägt?

In Ihrem Beispiel wird die Stapelverfolgung gedruckt, damit Sie zumindest wissen, was die Hauptursache gewesen sein könnte. In größeren Softwareprojekten ist es besser, diese Dinge zu protokollieren. Und hoffen wir, dass die Protokollkomponente auch keine Ausnahmen auslöst. Sie könnten in eine Endlosschleife geraten (die wahrscheinlich Ihre JVM töten wird).


0

Wenn Sie eine aktivierte Ausnahme haben und diese nicht in einer Methode behandeln möchten, sollte die Methode nur die Ausnahme auslösen. Ausnahmen nur abfangen und behandeln, wenn Sie damit etwas Nützliches tun wollen. Nur zu protokollieren ist in meinem Buch nicht sehr nützlich, da Benutzer selten Zeit haben, Protokolle zu lesen, um nach Ausnahmen zu suchen oder zu wissen, was zu tun ist, wenn eine Ausnahme ausgelöst wird.

Obwohl das Umschließen der Ausnahme eine Option ist, würde ich Ihnen nicht empfehlen, dies zu tun, es sei denn; Sie lösen eine andere Ausnahme aus, die mit einer vorhandenen Schnittstelle übereinstimmt, oder es gibt wirklich keine Möglichkeit, eine solche Ausnahme auszulösen.

Übrigens: Wenn Sie eine aktivierte Ausnahme erneut auslösen möchten, können Sie dies mit tun

try {
   // do something
} catch (Throwable e) {
   // do something with the exception
   Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable
}

Hinweis: Wenn Sie dies tun, sollten Sie sicherstellen, dass die Throws-Deklaration für die Methode korrekt ist, da der Compiler dies in dieser Situation nicht für Sie tun kann.


Nur FTR: Thread#stop()wurde in der Zwischenzeit veraltet und deaktiviert (es wirft UnsupportedOperationException).
Maaartinus

-2

weil es eine bewährte Methode ist. Ich dachte, jeder wüsste es.
Aber die kalte Wahrheit ist, dass niemand wirklich versteht, wie man mit Ausnahmen arbeitet. Der C-Fehlerbehandlungsstil machte viel mehr Sinn.


1
Nur weil eine große Anzahl von Entwicklern keine Ausnahmen macht, wird die "Fehlerrückgabe" nicht besser. Die C-Methode zur Fehlerbehandlung war und ist ein schrecklich fehleranfälliger (Wortspiel beabsichtigter) Mechanismus. Das Verschlucken von Ausnahmen (was in diesem Beispiel nur nicht stillschweigend der Fall ist) entspricht dem Aufrufen einer Funktion in C, ohne den Rückgabewert zu überprüfen. Das Endergebnis ist das gleiche - das Programm "fehlerhaft" an. Dieses Beispiel ist nur ein Fall, in dem die Java-Fehlerbehandlung als schlecht eingestuft wird C.
Lawrence Dol
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.