Wann werden aktivierte und nicht aktivierte Ausnahmen ausgewählt?

213

Wie entscheiden Sie in Java (oder einer anderen Sprache mit aktivierten Ausnahmen) beim Erstellen einer eigenen Ausnahmeklasse, ob diese aktiviert oder deaktiviert werden soll?

Mein Instinkt ist zu sagen, dass eine aktivierte Ausnahme in Fällen erforderlich ist, in denen der Anrufer möglicherweise auf produktive Weise wiederhergestellt werden kann, während eine nicht aktivierte Ausnahme eher für nicht behebbare Fälle gilt, aber ich würde mich für die Gedanken anderer interessieren.

Matt Sheppard
quelle
11
Barry Ruzek hat einen ausgezeichneten Leitfaden zur Auswahl von aktivierten oder nicht aktivierten Ausnahmen verfasst.
Sigget

Antworten:

241

Überprüfte Ausnahmen sind großartig, solange Sie verstehen, wann sie verwendet werden sollten. Die Java-Kern-API befolgt diese Regeln für SQLException (und manchmal für IOException) nicht, weshalb sie so schrecklich sind.

Überprüfte Ausnahmen sollten für vorhersehbare , aber nicht vermeidbare Fehler verwendet werden , deren Behebung angemessen ist .

Nicht aktivierte Ausnahmen sollten für alles andere verwendet werden.

Ich werde das für Sie aufschlüsseln, weil die meisten Leute falsch verstehen, was dies bedeutet.

  1. Vorhersehbar, aber nicht vermeidbar : Der Anrufer hat alles in seiner Macht Stehende getan, um die Eingabeparameter zu validieren, aber ein Zustand außerhalb seiner Kontrolle hat dazu geführt, dass der Vorgang fehlgeschlagen ist. Sie versuchen beispielsweise, eine Datei zu lesen, aber jemand löscht sie zwischen dem Zeitpunkt, an dem Sie prüfen, ob sie vorhanden ist, und dem Zeitpunkt, an dem der Lesevorgang beginnt. Indem Sie eine aktivierte Ausnahme deklarieren, weisen Sie den Anrufer an, diesen Fehler zu antizipieren.
  2. Angemessen, um sich zu erholen : Es macht keinen Sinn, Anrufern zu sagen, dass sie Ausnahmen antizipieren sollen, von denen sie sich nicht erholen können. Wenn ein Benutzer versucht, aus einer nicht vorhandenen Datei zu lesen, kann der Anrufer ihn zur Eingabe eines neuen Dateinamens auffordern. Wenn die Methode jedoch aufgrund eines Programmierfehlers (ungültige Methodenargumente oder fehlerhafte Methodenimplementierung) fehlschlägt, kann die Anwendung das Problem während der Ausführung nicht beheben. Das Beste, was es tun kann, ist, das Problem zu protokollieren und darauf zu warten, dass der Entwickler es zu einem späteren Zeitpunkt behebt.

Sofern die von Ihnen ausgelöste Ausnahme nicht alle oben genannten Bedingungen erfüllt, sollte eine nicht aktivierte Ausnahme verwendet werden.

Auf jeder Ebene neu bewerten : Manchmal ist die Methode, die die aktivierte Ausnahme abfängt, nicht der richtige Ort, um den Fehler zu behandeln. Überlegen Sie in diesem Fall, was für Ihre eigenen Anrufer angemessen ist. Wenn die Ausnahme vorhersehbar, nicht vermeidbar und für sie vernünftig ist, sollten Sie selbst eine aktivierte Ausnahme auslösen. Wenn nicht, sollten Sie die Ausnahme in eine nicht aktivierte Ausnahme einschließen. Wenn Sie diese Regel befolgen, werden Sie feststellen, dass Sie aktivierte Ausnahmen in nicht aktivierte Ausnahmen konvertieren und umgekehrt, je nachdem, in welcher Ebene Sie sich befinden.

Verwenden Sie für aktivierte und nicht aktivierte Ausnahmen die richtige Abstraktionsebene . Beispielsweise sollte ein Code-Repository mit zwei verschiedenen Implementierungen (Datenbank und Dateisystem) vermeiden, implementierungsspezifische Details durch Auslösen von SQLExceptionoder offenzulegen IOException. Stattdessen sollte die Ausnahme in eine Abstraktion eingeschlossen werden, die alle Implementierungen umfasst (z RepositoryException. B. ).

Gili
quelle
2
"Sie versuchen, eine Datei zu lesen, aber jemand löscht sie zwischen dem Zeitpunkt, an dem Sie prüfen, ob sie vorhanden ist, und dem Zeitpunkt, zu dem der Lesevorgang beginnt." => Wie wird das überhaupt "erwartet"? Für mich klingt es eher wie: Unerwartet und vermeidbar. Wer würde erwarten, dass eine Datei nur zwischen zwei Anweisungen gelöscht wird?
Koray Tugay
9
@KorayTugay Erwartet bedeutet nicht, dass das Szenario typisch ist. Dies bedeutet nur, dass wir vorhersehen können, dass dieser Fehler im Voraus auftritt (im Vergleich zu Programmierfehlern, die nicht im Voraus vorhergesagt werden können). Nicht vermeidbar bezieht sich auf die Tatsache, dass der Programmierer nichts tun kann, um zu verhindern, dass der Benutzer oder andere Anwendungen eine Datei zwischen dem Zeitpunkt, zu dem wir prüfen, ob sie vorhanden ist, und dem Zeitpunkt, zu dem der Lesevorgang beginnt, löschen.
Gili
Also müssen alle datenbankbezogenen Probleme innerhalb der Methode eine geprüfte Ausnahme auslösen?
ivanjermakov
59

Von einem Java-Lernenden :

Wenn eine Ausnahme auftritt, müssen Sie entweder die Ausnahme abfangen und behandeln oder dem Compiler mitteilen, dass Sie sie nicht behandeln können, indem Sie deklarieren, dass Ihre Methode diese Ausnahme auslöst. Dann muss der Code, der Ihre Methode verwendet, diese Ausnahme behandeln (auch sie) kann auch deklarieren, dass es die Ausnahme auslöst, wenn es nicht damit umgehen kann).

Der Compiler überprüft, ob wir eines der beiden Dinge getan haben (fangen oder deklarieren). Diese werden also als geprüfte Ausnahmen bezeichnet. Fehler und Laufzeitausnahmen werden vom Compiler jedoch nicht überprüft (obwohl Sie auswählen können, ob sie abgefangen oder deklariert werden sollen, ist dies nicht erforderlich). Diese beiden werden als ungeprüfte Ausnahmen bezeichnet.

Fehler werden verwendet, um die Bedingungen darzustellen, die außerhalb der Anwendung auftreten, z. B. ein Absturz des Systems. Laufzeitausnahmen treten normalerweise aufgrund eines Fehlers in der Anwendungslogik auf. In diesen Situationen können Sie nichts tun. Wenn eine Laufzeitausnahme auftritt, müssen Sie Ihren Programmcode neu schreiben. Diese werden also nicht vom Compiler überprüft. Diese Laufzeitausnahmen werden in der Entwicklungs- und Testphase aufgedeckt. Dann müssen wir unseren Code umgestalten, um diese Fehler zu entfernen.

Espo
quelle
13
Das ist die orthodoxe Sichtweise. Aber es gibt viele Kontroversen darüber.
Artbristol
49

Die Regel, die ich verwende, lautet: Verwenden Sie niemals ungeprüfte Ausnahmen! (oder wenn Sie keinen Weg daran vorbei sehen)

Es gibt ein sehr starkes Argument für das Gegenteil: Verwenden Sie niemals geprüfte Ausnahmen. Ich bin nicht bereit, Partei in der Debatte zu ergreifen, aber es scheint ein breiter Konsens darüber zu bestehen, dass die Einführung geprüfter Ausnahmen im Nachhinein eine falsche Entscheidung war. Bitte schießen Sie nicht auf den Boten und beziehen Sie sich auf diese Argumente .

Konrad Rudolph
quelle
3
IMHO, geprüfte Ausnahmen könnten ein großer Vorteil gewesen sein, wenn es eine einfache Möglichkeit für eine Methode gegeben hätte, zu deklarieren, dass sie nicht erwartet, dass in einem bestimmten Codeblock aufgerufene Methoden bestimmte Ausnahmen (oder überhaupt irgendwelche) auslösen, und dass alle geprüft werden Ausnahmen, die entgegen dieser Erwartung ausgelöst werden, sollten in einen anderen Ausnahmetyp eingeschlossen und erneut ausgelöst werden. Ich würde davon ausgehen, dass in 90% der Fälle, in denen Code nicht auf eine aktivierte Ausnahme vorbereitet ist, ein solches Wrap-and-Rethrow der beste Weg ist, damit umzugehen, aber da es keine Sprachunterstützung gibt, wird dies selten durchgeführt.
Supercat
@supercat Im Wesentlichen ist es das: Ich bin ein eingefleischter Fan der strengen Typprüfung und überprüfte Ausnahmen sind eine logische Erweiterung davon. Ich habe geprüfte Ausnahmen komplett aufgegeben, obwohl ich sie konzeptionell sehr mag.
Konrad Rudolph
1
Ein Ärger, den ich mit dem Entwurf von Ausnahmen habe, den der von mir beschriebene Mechanismus lösen würde, ist, dass, wenn foodokumentiert wird, dass barExceptionbeim Lesen über das Ende einer Datei hinaus geworfen wird , und fooeine Methode aufgerufen wird, die auslöst barException, obwohl dies foonicht erwartet wird Code, der aufruft foo, denkt, dass das Ende der Datei erreicht wurde, und hat keine Ahnung, dass etwas Unerwartetes passiert ist. Ich würde diese Situation betrachten als derjenige zu sein , wo überprüft Ausnahmen sollten am nützlichsten sein, aber es ist auch der einzige Fall , in dem der Compiler nicht behandelte geprüfte Ausnahmen erlaubt.
Supercat
@supercat: Es gibt bereits eine einfache Möglichkeit für Code, das zu tun, was Sie im ersten Kommentar wollen: Wickeln Sie den Code in einen try-Block, fangen Sie Exception ab und verpacken Sie die Exception in eine RuntimeException und werfen Sie sie erneut.
Warren Dew
1
@KonradRudolph Supercat bezeichnet "einen bestimmten Codeblock"; Da der Block definiert werden muss, würde die deklarative Syntax das Aufblähen nicht wesentlich reduzieren. Wenn Sie der Meinung sind, dass dies für die gesamte Funktion deklarativ ist, würde dies zu einer schlechten Programmierung führen, da die Benutzer nur an der Deklaration festhalten, anstatt sich die überprüften Ausnahmen anzusehen, die möglicherweise abgefangen werden, und sicherstellen, dass es keinen anderen besseren Weg gibt um mit ihnen umzugehen.
Warren Dew
46

Auf jedem ausreichend großen System mit vielen Ebenen sind geprüfte Ausnahmen nutzlos, da Sie ohnehin eine Strategie auf Architekturebene benötigen, um zu behandeln, wie die Ausnahme behandelt wird (verwenden Sie eine Fehlersperre).

Mit aktivierten Ausnahmen ist Ihre Fehlerbehandlungsstrategie mikro-verwaltet und auf jedem großen System unerträglich.

Meistens wissen Sie nicht, ob ein Fehler "behebbar" ist, da Sie nicht wissen, in welcher Schicht sich der Aufrufer Ihrer API befindet.

Angenommen, ich erstelle eine StringToInt-API, die die Zeichenfolgendarstellung einer Ganzzahl in eine Int konvertiert. Muss ich eine aktivierte Ausnahme auslösen, wenn die API mit der Zeichenfolge "foo" aufgerufen wird? Ist es wiederherstellbar? Ich weiß es nicht, da der Aufrufer meiner StringToInt-API in seiner Ebene die Eingabe möglicherweise bereits validiert hat. Wenn diese Ausnahme ausgelöst wird, handelt es sich entweder um einen Fehler oder eine Datenbeschädigung, die für diese Ebene nicht wiederhergestellt werden kann.

In diesem Fall möchte der Aufrufer der API die Ausnahme nicht abfangen. Er will nur die Ausnahme "sprudeln lassen". Wenn ich eine aktivierte Ausnahme ausgewählt habe, verfügt dieser Aufrufer über viele nutzlose Catch-Blöcke, um die Ausnahme künstlich erneut auszulösen.

Was wiederherstellbar ist, hängt die meiste Zeit vom Aufrufer der API ab, nicht vom Writer der API. Eine API sollte keine aktivierten Ausnahmen verwenden, da nur nicht aktivierte Ausnahmen es ermöglichen, eine Ausnahme entweder abzufangen oder zu ignorieren.

Stephane
quelle
3
Dies ist sehr nah an userstories.blogspot.com/2008/12/…
alexsmail
15
@alexsmail Internet überrascht mich immer wieder, eigentlich ist es mein Blog :)
Stephane
30

Du hast Recht.

Ungeprüfte Ausnahmen werden verwendet, um das System schnell ausfallen zu lassen, was eine gute Sache ist. Sie sollten klar angeben, was Ihre Methode erwartet, um richtig zu funktionieren. Auf diese Weise können Sie die Eingabe nur einmal validieren.

Zum Beispiel:

/**
 * @params operation - The operation to execute.
 * @throws IllegalArgumentException if the operation is "exit"
 */
 public final void execute( String operation ) {
     if( "exit".equals(operation)){
          throw new IllegalArgumentException("I told you not to...");
     }
     this.operation = operation; 
     .....  
 }
 private void secretCode(){
      // we perform the operation.
      // at this point the opreation was validated already.
      // so we don't worry that operation is "exit"
      .....  
 }

Nur um ein Beispiel zu nennen. Der Punkt ist, wenn das System schnell ausfällt, wissen Sie, wo und warum es ausgefallen ist. Sie erhalten eine Stapelverfolgung wie:

 IllegalArgumentException: I told you not to use "exit" 
 at some.package.AClass.execute(Aclass.java:5)
 at otherPackage.Otherlass.delegateTheWork(OtherClass.java:4569)
 ar ......

Und du wirst wissen, was passiert ist. Die OtherClass in der Methode "delegateTheWork" (in Zeile 4569) hat Ihre Klasse mit dem Wert "exit" aufgerufen, auch wenn dies nicht der Fall sein sollte.

Andernfalls müssten Sie Validierungen über Ihren gesamten Code streuen, und das ist fehleranfällig. Außerdem ist es manchmal schwierig zu verfolgen, was schief gelaufen ist, und Sie können stundenlanges frustrierendes Debuggen erwarten

Gleiches passiert mit NullPointerExceptions. Wenn Sie eine 700-Zeilen-Klasse mit etwa 15 Methoden haben, die 30 Attribute verwendet und keine davon null sein kann, können Sie alle diese Attribute schreibgeschützt machen und sie im Konstruktor oder validieren, anstatt sie in jeder dieser Methoden auf Nullfähigkeit zu überprüfen Fabrikmethode.

 public static MyClass createInstane( Object data1, Object data2 /* etc */ ){ 
      if( data1 == null ){ throw NullPointerException( "data1 cannot be null"); }

  }


  // the rest of the methods don't validate data1 anymore.
  public void method1(){ // don't worry, nothing is null 
      ....
  }
  public void method2(){ // don't worry, nothing is null 
      ....
  }
  public void method3(){ // don't worry, nothing is null 
      ....
  }

Überprüfte Ausnahmen Sind nützlich, wenn der Programmierer (Sie oder Ihre Mitarbeiter) alles richtig gemacht, die Eingabe validiert, Tests ausgeführt und der gesamte Code perfekt ist, der Code jedoch eine Verbindung zu einem Webservice eines Drittanbieters herstellt, der möglicherweise nicht verfügbar ist (oder zu einer Datei) Sie verwendeten wurde von einem anderen externen Prozess gelöscht usw.). Der Webservice kann sogar validiert werden, bevor die Verbindung versucht wird, aber während der Datenübertragung ist ein Fehler aufgetreten.

In diesem Szenario können Sie oder Ihre Mitarbeiter nichts dagegen tun. Trotzdem müssen Sie etwas tun und dürfen die Anwendung nicht einfach sterben und in den Augen des Benutzers verschwinden lassen. Sie verwenden dafür eine aktivierte Ausnahme und behandeln die Ausnahme. Was können Sie in diesem Fall tun? Meistens, nur um zu versuchen, den Fehler zu protokollieren, speichern Sie wahrscheinlich Ihre Arbeit (die App-Arbeit) und präsentieren dem Benutzer eine Nachricht . (Die Seite blabla ist nicht verfügbar, bitte versuchen Sie es später erneut usw.)

Wenn die aktivierte Ausnahme überbeansprucht wird (durch Hinzufügen der "Ausnahme auslösen" in allen Methodensignaturen), wird Ihr Code sehr zerbrechlich, da jeder diese Ausnahme ignoriert (weil sie zu allgemein ist) und die Qualität des Codes ernsthaft ist kompromittiert.

Wenn Sie die ungeprüfte Ausnahme überbeanspruchen, passiert etwas Ähnliches. Die Benutzer dieses Codes wissen nicht, ob etwas schief gehen kann, und es werden viele Versuche {...} catch (Throwable t) angezeigt.

OscarRyz
quelle
2
Gut gesagt! +1. Es überrascht mich immer wieder, dass dieser Unterscheidungsrufer (nicht markiert) / Angerufene (aktiviert) nicht offensichtlicher ist ...
VonC
19

Hier ist meine 'letzte Faustregel'.
Ich benutze:

Im Vergleich zur vorherigen Antwort ist dies eine klare Begründung (auf die man sich einigen oder nicht einigen kann) für die Verwendung der einen oder anderen (oder beider) Art von Ausnahmen.


Für beide Ausnahmen erstelle ich meine eigene ungeprüfte und geprüfte Ausnahme für meine Anwendung (eine gute Praxis, wie hier erwähnt ), mit Ausnahme der sehr häufigen ungeprüften Ausnahme (wie NullPointerException).

So besteht das Ziel dieser speziellen Funktion im Folgenden darin, ein Objekt zu erstellen (oder zu erhalten, falls es bereits vorhanden ist),
was bedeutet:

  • Der Container des Objekts, das erstellt / abgerufen werden soll, MUSS vorhanden sein (Verantwortung des Anrufers
    => nicht Ausnahme UND eindeutiger Javadoc-Kommentar für diese aufgerufene Funktion).
  • Die anderen Parameter können nicht null sein
    (Auswahl des Codierers, der diesen auf den CALLER setzt: Der Codierer prüft nicht auf den Nullparameter, aber der Codierer DOKUMENTIERT ES)
  • Das Ergebnis kann nicht null sein
    (Verantwortung und Auswahl des Codes des Angerufenen, Auswahl, die für den Anrufer von großem Interesse ist
    => geprüfte Ausnahme, da jeder Anrufer eine Entscheidung treffen MUSS, wenn das Objekt nicht erstellt / gefunden werden kann, und das Die Entscheidung muss zum Zeitpunkt der Kompilierung erzwungen werden: Sie können diese Funktion nicht verwenden, ohne sich mit dieser Möglichkeit befassen zu müssen (dh mit dieser aktivierten Ausnahme).

Beispiel:


/**
 * Build a folder. <br />
 * Folder located under a Parent Folder (either RootFolder or an existing Folder)
 * @param aFolderName name of folder
 * @param aPVob project vob containing folder (MUST NOT BE NULL)
 * @param aParent parent folder containing folder 
 *        (MUST NOT BE NULL, MUST BE IN THE SAME PVOB than aPvob)
 * @param aComment comment for folder (MUST NOT BE NULL)
 * @return a new folder or an existing one
 * @throws CCException if any problems occurs during folder creation
 * @throws AssertionFailedException if aParent is not in the same PVob
 * @throws NullPointerException if aPVob or aParent or aComment is null
 */
static public Folder makeOrGetFolder(final String aFoldername, final Folder aParent,
    final IPVob aPVob, final Comment aComment) throws CCException {
    Folder aFolderRes = null;
    if (aPVob.equals(aParent.getPVob() == false) { 
       // UNCHECKED EXCEPTION because the caller failed to live up
       // to the documented entry criteria for this function
       Assert.isLegal(false, "parent Folder must be in the same PVob than " + aPVob); }

    final String ctcmd = "mkfolder " + aComment.getCommentOption() + 
        " -in " + getPNameFromRepoObject(aParent) + " " + aPVob.getFullName(aFolderName);

    final Status st = getCleartool().executeCmd(ctcmd);

    if (st.status || StringUtils.strictContains(st.message,"already exists.")) {
        aFolderRes = Folder.getFolder(aFolderName, aPVob);
    }
    else {
        // CHECKED EXCEPTION because the callee failed to respect his contract
        throw new CCException.Error("Unable to make/get folder '" + aFolderName + "'");
    }
    return aFolderRes;
}
VonC
quelle
19

Es geht nicht nur um die Fähigkeit, sich von der Ausnahme zu erholen. Am wichtigsten ist meiner Meinung nach, ob der Anrufer daran interessiert ist, die Ausnahme abzufangen oder nicht.

Wenn Sie eine Bibliothek schreiben, die an anderer Stelle verwendet werden soll, oder eine untergeordnete Ebene in Ihrer Anwendung, fragen Sie sich, ob der Anrufer daran interessiert ist, Ihre Ausnahme zu erkennen (zu kennen). Wenn dies nicht der Fall ist, verwenden Sie eine ungeprüfte Ausnahme, damit Sie ihn nicht unnötig belasten.

Dies ist die Philosophie, die von vielen Frameworks verwendet wird. Insbesondere Frühling und Ruhezustand kommen in den Sinn - sie konvertieren bekannte geprüfte Ausnahmen in ungeprüfte Ausnahmen, gerade weil geprüfte Ausnahmen in Java überbeansprucht werden. Ein Beispiel, an das ich denken kann, ist die JSONException von json.org, eine überprüfte Ausnahme, die meistens ärgerlich ist - sie sollte deaktiviert sein, aber der Entwickler hat sie einfach nicht durchdacht.

Übrigens hängt das Interesse des Anrufers an der Ausnahme meistens direkt mit der Fähigkeit zusammen, sich von der Ausnahme zu erholen, aber das ist nicht immer der Fall.

Yoni
quelle
13

Hier ist eine sehr einfache Lösung für Ihr geprüftes / nicht geprüftes Dilemma.

Regel 1: Stellen Sie sich eine ungeprüfte Ausnahme als testbare Bedingung vor, bevor Code ausgeführt wird. beispielsweise…

x.doSomething(); // the code throws a NullPointerException

wobei x null ist ... ... sollte der Code möglicherweise Folgendes haben ...

if (x==null)
{
    //do something below to make sure when x.doSomething() is executed, it won’t throw a NullPointerException.
    x = new X();
}
x.doSomething();

Regel 2: Stellen Sie sich eine überprüfte Ausnahme als eine nicht testbare Bedingung vor, die auftreten kann, während der Code ausgeführt wird.

Socket s = new Socket(“google.com”, 80);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();

… Im obigen Beispiel ist die URL (google.com) möglicherweise nicht verfügbar, da der DNS-Server nicht verfügbar ist. Selbst in dem Moment, in dem der DNS-Server funktionierte und den Namen "google.com" in eine IP-Adresse auflöste, konnte das Netzwerk jederzeit ausfallen, wenn die Verbindung zu google.com hergestellt wurde. Sie können das Netzwerk einfach nicht die ganze Zeit testen, bevor Sie Streams lesen und schreiben.

Es gibt Zeiten, in denen der Code einfach ausgeführt werden muss, bevor wir feststellen können, ob ein Problem vorliegt. Indem ich Entwickler dazu zwinge, ihren Code so zu schreiben, dass sie gezwungen sind, mit diesen Situationen über Checked Exception umzugehen, muss ich meinen Hut vor dem Schöpfer von Java ziehen, der dieses Konzept erfunden hat.

Im Allgemeinen folgen fast alle APIs in Java den beiden oben genannten Regeln. Wenn Sie versuchen, in eine Datei zu schreiben, wird die Festplatte möglicherweise voll, bevor der Schreibvorgang abgeschlossen wird. Möglicherweise haben andere Prozesse dazu geführt, dass die Festplatte voll wurde. Es gibt einfach keine Möglichkeit, diese Situation zu testen. Für diejenigen, die mit Hardware interagieren, bei denen die Verwendung der Hardware jederzeit fehlschlagen kann, scheinen Checked Exceptions eine elegante Lösung für dieses Problem zu sein.

Dies hat eine Grauzone. Für den Fall, dass viele Tests erforderlich sind (eine umwerfende if-Anweisung mit vielen && und ||), wird als Ausnahme eine CheckedException ausgelöst, einfach weil es zu schmerzhaft ist, um es richtig zu machen - Sie können dieses Problem einfach nicht sagen ist ein Programmierfehler. Wenn es viel weniger als 10 Tests gibt (z. B. 'if (x == null)'), sollte der Programmiererfehler eine UncheckedException sein.

Im Umgang mit Sprachdolmetschern wird es interessant. Sollte ein Syntaxfehler gemäß den oben genannten Regeln als geprüfte oder ungeprüfte Ausnahme betrachtet werden? Ich würde argumentieren, dass wenn die Syntax der Sprache getestet werden kann, bevor sie ausgeführt wird, es eine UncheckedException sein sollte. Wenn die Sprache nicht getestet werden kann - ähnlich wie Assembler-Code auf einem PC ausgeführt wird - sollte der Syntaxfehler eine überprüfte Ausnahme sein.

Die beiden oben genannten Regeln werden wahrscheinlich 90% Ihrer Bedenken beseitigen, aus denen Sie auswählen können. Um die Regeln zusammenzufassen, folgen Sie diesem Muster… 1) Wenn der auszuführende Code getestet werden kann, bevor er ausgeführt wird, damit er ordnungsgemäß ausgeführt wird, und wenn eine Ausnahme auftritt - auch bekannt als Programmiererfehler -, sollte die Ausnahme eine UncheckedException (eine Unterklasse von RuntimeException) sein ). 2) Wenn der auszuführende Code nicht getestet werden kann, bevor er ausgeführt wird, damit er ordnungsgemäß ausgeführt wird, sollte die Ausnahme eine geprüfte Ausnahme sein (eine Unterklasse der Ausnahme).

user3598189
quelle
9

Sie können es als aktivierte oder nicht aktivierte Ausnahme bezeichnen. aber beide können Arten von Ausnahmen vom Programmierer aufgefangen werden, so dass die beste Antwort ist: Schreiben Sie alle Ihre Ausnahmen als nicht markiert und dokumentieren sie. Auf diese Weise kann der Entwickler, der Ihre API verwendet, auswählen, ob er diese Ausnahme abfangen und etwas unternehmen möchte. Überprüfte Ausnahmen sind eine reine Zeitverschwendung und machen Ihren Code zu einem schockierenden Albtraum. Durch ordnungsgemäße Komponententests werden dann alle Ausnahmen angezeigt, mit denen Sie möglicherweise etwas anfangen müssen.

Colin Saxton
quelle
1
+1 Um zu erwähnen, dass Unit-Tests ein besserer Weg sind, um das Problem zu lösen, sollen geprüfte Ausnahmen gelöst werden.
Keith Pinson
+1 für Unit-Tests. Die Verwendung von aktivierten / nicht überprüften Ausnahmen hat nur geringe Auswirkungen auf die Codequalität. Das Argument, dass wenn man geprüfte Ausnahmen verwendet, dies zu einer besseren Codequalität führen würde, ist also ein völlig falsches Argument!
user1697575
7

Überprüfte Ausnahme: Wenn der Client eine Ausnahme wiederherstellen kann und fortfahren möchte, verwenden Sie die aktivierte Ausnahme.

Ungeprüfte Ausnahme: Wenn ein Client nach der Ausnahme nichts mehr tun kann, lösen Sie eine ungeprüfte Ausnahme aus.

Beispiel: Wenn von Ihnen erwartet wird, dass Sie eine arithmetische Operation in einer Methode A () ausführen und auf der Ausgabe von A () basieren, müssen Sie eine andere Operation ausführen. Wenn die Ausgabe von Methode A () null ist, die Sie zur Laufzeit nicht erwarten, wird erwartet, dass Sie eine Nullzeigerausnahme auslösen, bei der es sich um eine Laufzeitausnahme handelt.

Siehe hier

Ziele erreichen
quelle
2

Ich stimme der Präferenz für ungeprüfte Ausnahmen in der Regel zu, insbesondere beim Entwerfen einer API. Der Anrufer kann jederzeit eine dokumentierte, nicht aktivierte Ausnahme abfangen. Sie zwingen den Anrufer einfach nicht unnötig dazu.

Ich finde geprüfte Ausnahmen auf der unteren Ebene als Implementierungsdetail nützlich. Es scheint oft ein besserer Kontrollfluss zu sein, als einen bestimmten Fehler "Rückkehrcode" verwalten zu müssen. Es kann manchmal hilfreich sein, die Auswirkungen einer Idee für eine Codeänderung auf niedriger Ebene zu erkennen. Deklarieren Sie eine aktivierte Ausnahme nachgeschaltet und sehen Sie, wer angepasst werden muss. Dieser letzte Punkt gilt nicht, wenn es viele generische gibt: catch (Ausnahme e) oder löst eine Ausnahme aus, die normalerweise sowieso nicht allzu gut durchdacht ist.

Scott Kurz
quelle
2

Hier möchte ich meine Meinung mitteilen, die ich nach langjähriger Entwicklungserfahrung habe:

  1. Geprüfte Ausnahme. Dies ist ein Teil des Geschäftsanwendungsfalls oder des Anrufverlaufs. Dies ist ein Teil der Anwendungslogik, die wir erwarten oder nicht erwarten. Zum Beispiel Verbindung abgelehnt, Bedingung ist nicht erfüllt usw. Wir müssen damit umgehen und dem Benutzer die entsprechende Nachricht mit Anweisungen anzeigen, was passiert ist und was als nächstes zu tun ist (versuchen Sie es später erneut usw.). Normalerweise nenne ich es Nachbearbeitungsausnahme oder "Benutzer" -Ausnahme.

  2. Deaktivierte Ausnahme. Dies ist ein Teil der Programmierausnahme, ein Fehler in der Software-Code-Programmierung (Fehler, Defekt) und spiegelt eine Art und Weise wider, wie Programmierer die API gemäß Dokumentation verwenden müssen. Wenn ein externes lib / Framework-Dokument angibt, dass Daten in einem bestimmten Bereich und nicht null abgerufen werden sollen, da NPE oder IllegalArgumentException ausgelöst werden, sollte der Programmierer dies erwarten und die API gemäß Dokumentation korrekt verwenden. Andernfalls wird die Ausnahme ausgelöst. Normalerweise nenne ich es Vorverarbeitungsausnahme oder "Validierungs" -Ausnahme.

Nach Zielgruppe. Lassen Sie uns nun über die Zielgruppe oder Personengruppe sprechen, für die die Ausnahmen festgelegt wurden (meiner Meinung nach):

  1. Geprüfte Ausnahme. Zielgruppe sind Benutzer / Kunden.
  2. Deaktivierte Ausnahme. Zielgruppe sind Entwickler. Mit anderen Worten, deaktivierte Ausnahmen sind nur für Entwickler vorgesehen.

Nach Anwendungsentwicklungs-Lebenszyklusphase.

  1. Die aktivierte Ausnahme ist so konzipiert, dass sie während des gesamten Produktionslebenszyklus als normaler und erwarteter Mechanismus besteht, den eine Anwendung in Ausnahmefällen behandelt.
  2. Die nicht aktivierte Ausnahme ist nur für die Anwendungsentwicklung / den Testlebenszyklus vorgesehen. Sie sollten alle während dieser Zeit behoben und nicht ausgelöst werden, wenn eine Anwendung bereits in der Produktion ausgeführt wird.

Der Grund, warum Frameworks normalerweise ungeprüfte Ausnahmen verwenden (z. B. Spring), ist, dass das Framework die Geschäftslogik Ihrer Anwendung nicht bestimmen kann. Es liegt an den Entwicklern, diese zu erfassen und ihre eigene Logik zu entwerfen.

Denys
quelle
2

Wir müssen diese beiden Arten von Ausnahmen danach unterscheiden, ob es sich um einen Programmiererfehler handelt oder nicht.

  • Wenn ein Fehler ein Programmiererfehler ist, muss es sich um eine ungeprüfte Ausnahme handeln . Zum Beispiel: SQLException / IOException / NullPointerException. Diese Ausnahmen sind Programmierfehler. Sie sollten vom Programmierer gehandhabt werden. In der JDBC-API ist SQLException eine geprüfte Ausnahme. In Spring JDBCTemplate handelt es sich um eine ungeprüfte Ausnahme. Der Programmierer macht sich bei Verwendung von Spring keine Sorgen um die SqlException.
  • Wenn ein Fehler kein Programmiererfehler ist und der Grund von außen kommt, muss es sich um eine überprüfte Ausnahme handeln. Beispiel: Wenn die Datei gelöscht oder die Dateiberechtigung von einer anderen Person geändert wird, sollte sie wiederhergestellt werden.

FileNotFoundException ist ein gutes Beispiel, um subtile Unterschiede zu verstehen. FileNotFoundException wird ausgelöst, falls die Datei nicht gefunden wird. Es gibt zwei Gründe für diese Ausnahme. Wenn der Dateipfad vom Entwickler definiert oder vom Endbenutzer über die GUI übernommen wird, sollte es sich um eine nicht aktivierte Ausnahme handeln. Wenn die Datei von einer anderen Person gelöscht wird, sollte es sich um eine überprüfte Ausnahme handeln.

Checked Exception kann auf zwei Arten behandelt werden. Diese verwenden try-catch oder verbreiten die Ausnahme. Im Falle der Weitergabe von Ausnahmen werden alle Methoden im Aufrufstapel eng miteinander verbunden aufgrund der Ausnahmebehandlung . Deshalb müssen wir Checked Exception sorgfältig verwenden.

Wenn Sie ein mehrschichtiges Unternehmenssystem entwickeln, müssen Sie die meist ungeprüfte Ausnahme zum Auslösen auswählen. Vergessen Sie jedoch nicht, die aktivierte Ausnahme für den Fall zu verwenden, dass Sie nichts tun können.

ailhanli
quelle
1

Aktivierte Ausnahmen sind nützlich für wiederherstellbare Fälle, in denen Sie dem Anrufer Informationen bereitstellen möchten (z. B. unzureichende Berechtigungen, nicht gefundene Datei usw.).

Nicht aktivierte Ausnahmen werden selten oder gar nicht verwendet, um den Benutzer oder Programmierer zur Laufzeit über schwerwiegende Fehler oder unerwartete Bedingungen zu informieren. Werfen Sie sie nicht, wenn Sie Code oder Bibliotheken schreiben, die von anderen verwendet werden, da sie möglicherweise nicht erwarten, dass Ihre Software ungeprüfte Ausnahmen auslöst, da der Compiler nicht erzwingt, dass sie abgefangen oder deklariert werden.

David Crow
quelle
Ich bin nicht einverstanden mit Ihrer Aussage "Ungeprüfte Ausnahmen werden selten, wenn überhaupt verwendet", tatsächlich sollte es umgekehrt sein! Verwenden Sie standardmäßig nicht aktivierte Ausdrücke, wenn Sie die Hierarchie der Anwendungsausnahmen entwerfen. Lassen Sie Entwickler entscheiden, wann sie die Ausnahme behandeln möchten (z. B. müssen sie keine catch-Blöcke oder Throws-Klauseln setzen, wenn sie nicht wissen, wie sie damit umgehen sollen).
user1697575
1

Wenn eine Ausnahme weniger wahrscheinlich erwartet wird, können wir auch nach dem Abfangen fortfahren, und wir können nichts tun, um diese Ausnahme zu vermeiden, dann können wir die aktivierte Ausnahme verwenden.

Wann immer wir etwas Sinnvolles tun wollen, wenn eine bestimmte Ausnahme auftritt und wenn diese Ausnahme erwartet, aber nicht sicher ist, können wir die aktivierte Ausnahme verwenden.

Wenn eine Ausnahme in verschiedenen Ebenen navigiert, muss sie nicht in jeder Ebene abgefangen werden. In diesem Fall können wir die Laufzeitausnahme oder die Wrap-Ausnahme als nicht aktivierte Ausnahme verwenden.

Eine Laufzeitausnahme wird verwendet, wenn eine Ausnahme am wahrscheinlichsten auftritt, es keine Möglichkeit gibt, weiterzugehen, und nichts wiederherstellbar ist. In diesem Fall können wir also Vorsichtsmaßnahmen in Bezug auf diese Ausnahme treffen. EX: NUllPointerException, ArrayOutofBoundsException. Dies ist wahrscheinlicher. In diesem Szenario können wir beim Codieren Vorsichtsmaßnahmen treffen, um solche Ausnahmen zu vermeiden. Andernfalls müssen wir überall try catch-Blöcke schreiben.

Allgemeinere Ausnahmen können deaktiviert werden, weniger allgemeine werden aktiviert.

ramu p
quelle
1

Ich denke, wir können über Ausnahmen von mehreren Fragen nachdenken:

Warum kommt es zu Ausnahmen? Was können wir tun, wenn es passiert?

aus Versehen ein Fehler. B. wird eine Methode des Nullobjekts aufgerufen.

String name = null;
... // some logics
System.out.print(name.length()); // name is still null here

Diese Art von Ausnahme sollte während des Tests behoben werden. Andernfalls wird die Produktion unterbrochen und es liegt ein sehr hoher Fehler vor, der sofort behoben werden muss. Diese Art von Ausnahmen muss nicht überprüft werden.

Durch Eingabe von externen Diensten können Sie die Ausgabe von externen Diensten nicht steuern oder ihnen nicht vertrauen.

String name = ExternalService.getName(); // return null
System.out.print(name.length());    // name is null here

Hier müssen Sie möglicherweise überprüfen, ob der Name null ist, wenn Sie fortfahren möchten, wenn er null ist. Andernfalls können Sie ihn in Ruhe lassen, und er stoppt hier und gibt dem Aufrufer die Laufzeitausnahme. Diese Art von Ausnahmen muss nicht überprüft werden.

Aufgrund einer Laufzeitausnahme von extern können Sie den externen Dienst nicht steuern oder ihm vertrauen.

Hier müssen Sie möglicherweise alle Ausnahmen von ExternalService abfangen, wenn Sie fortfahren möchten, wenn dies geschieht. Andernfalls können Sie es in Ruhe lassen, und es wird hier gestoppt und gibt dem Aufrufer die Laufzeitausnahme.

Durch die aktivierte Ausnahme von extern können Sie den externen Dienst nicht steuern oder ihm vertrauen.

Hier müssen Sie möglicherweise alle Ausnahmen von ExternalService abfangen, wenn Sie fortfahren möchten, wenn dies geschieht. Andernfalls können Sie es in Ruhe lassen, und es wird hier gestoppt und gibt dem Aufrufer die Laufzeitausnahme.

Müssen wir in diesem Fall wissen, welche Art von Ausnahme in ExternalService aufgetreten ist? Es hängt davon ab, ob:

  1. Wenn Sie einige Arten von Ausnahmen behandeln können, müssen Sie sie abfangen und verarbeiten. Für andere sprudeln sie.

  2. Wenn Sie ein Protokoll oder eine Antwort auf die bestimmte Ausführung des Benutzers benötigen, können Sie diese abfangen. Für andere sprudeln sie.

Jacky
quelle
0

Ich denke, wenn Sie Application Exception deklarieren, sollte es Unchecked Exception sein, dh die Unterklasse von RuntimeException. Der Grund dafür ist, dass der Anwendungscode nicht mit try-catch unübersichtlich wird und die Methode deklariert wird. Wenn Ihre Anwendung Java Api verwendet, werden geprüfte Ausnahmen ausgelöst, die ohnehin behandelt werden müssen. In anderen Fällen kann die Anwendung eine ungeprüfte Ausnahme auslösen. Wenn der Anwendungsaufrufer weiterhin ungeprüfte Ausnahmen behandeln muss, kann dies durchgeführt werden.

Akash Aggarwal
quelle
-12

Die Regel, die ich verwende, lautet: Verwenden Sie niemals ungeprüfte Ausnahmen! (oder wenn Sie keinen Weg daran vorbei sehen)

Aus der Sicht des Entwicklers, der Ihre Bibliothek verwendet, oder des Endbenutzers, der Ihre Bibliothek / Anwendung verwendet, ist es wirklich schade, mit einer Anwendung konfrontiert zu werden, die aufgrund einer nicht erworbenen Ausnahme abstürzt. Und auf ein Allheilmittel zu zählen, ist auch nicht gut.

Auf diese Weise kann dem Endbenutzer weiterhin eine Fehlermeldung angezeigt werden, anstatt dass die Anwendung vollständig verschwindet.


quelle
1
Sie erklären nicht, was Ihrer Meinung nach mit einem Catch-All für die meisten ungeprüften Ausnahmen falsch ist.
Matthew Flaschen
Stimmen Sie