756 Stimmen

Überprüfte und ungeprüfte Ausnahmen in Java verstehen

Joshua Bloch in " Leistungsfähiges Java " sagte, dass

Verwenden Sie geprüfte Ausnahmen für wiederherstellbare Bedingungen und Laufzeit Ausnahmen für Programmierfehler (Punkt 58 in der 2. Auflage)

Mal sehen, ob ich das richtig verstehe.

Hier ist mein Verständnis einer geprüften Ausnahme:

try{
    String userInput = //read in user input
    Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
    id = 0; //recover the situation by setting the id to 0
}

1. Handelt es sich um eine kontrollierte Ausnahme?

2. Ist RuntimeException eine ungeprüfte Ausnahme?

Hier ist mein Verständnis einer ungeprüften Ausnahme:

try{
    File file = new File("my/file/path");
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){

//3. What should I do here?
    //Should I "throw new FileNotFoundException("File not found");"?
    //Should I log?
    //Or should I System.exit(0);?
}

4. Könnte der obige Code nicht auch eine geprüfte Ausnahme sein? Kann ich versuchen, die Situation auf diese Weise wiederherzustellen? Kann ich das? (Anmerkung: meine 3. Frage befindet sich innerhalb der catch oben)

try{
    String filePath = //read in from user input file path
    File file = new File(filePath);
    FileInputStream fis = new FileInputStream(file);   
}catch(FileNotFoundException e){
    //Kindly prompt the user an error message
    //Somehow ask the user to re-enter the file path.
}

5. Warum tun die Menschen das?

public void someMethod throws Exception{

}

Warum lassen sie die Ausnahme aufsprudeln? Ist es nicht besser, den Fehler früher zu behandeln? Warum aufblähen?

6. Soll ich die genaue Ausnahme aufblasen oder sie mit Exception maskieren?

Im Folgenden finden Sie meine Lektüre

Wann sollte ich in Java eine geprüfte Ausnahme erstellen, und wann eine Laufzeitausnahme?

Wann sind kontrollierte und nicht kontrollierte Ausnahmen zu wählen?

8 Stimmen

Ich habe ein gutes Beispiel für eine ungeprüfte Ausnahme. Ich habe eine DataSeries Klasse, die Daten enthält, die immer in zeitlicher Reihenfolge bleiben müssen. Es gibt eine Methode zum Hinzufügen einer neuen DataPoint an das Ende einer DataSeries . Wenn mein gesamter Code im gesamten Projekt korrekt funktioniert, ist eine DataPoint sollte niemals an das Ende angefügt werden, das ein älteres Datum als das bereits vorhandene hat. Jedes Modul im gesamten Projekt ist nach dieser Binsenweisheit aufgebaut. Ich überprüfe jedoch diese Bedingung und werfe eine ungeprüfte Ausnahme, wenn sie eintritt. Und warum? Wenn es passiert, möchte ich wissen, wer das macht, und es beheben.

3 Stimmen

Um noch mehr Verwirrung zu stiften. Viele Leute haben vor ~10 Jahren geprüfte Ausnahmen befürwortet, aber die Ansicht geht heute mehr und mehr in Richtung "geprüfte Ausnahmen sind schlecht". (Ich stimme dem allerdings nicht zu)

0 Stimmen

3voto

JAVA Punkte 506

Warum lassen sie die Ausnahme aufsprudeln? Ist es nicht besser, den Fehler früher zu behandeln? Warum aufblähen?

Nehmen wir zum Beispiel an, Sie haben einige Client-Server-Anwendung und der Client eine Anfrage für eine Ressource gestellt hat, die nicht gefunden werden konnte, oder für einen anderen Fehler, der auf der Serverseite während der Verarbeitung der Benutzeranfrage aufgetreten ist, dann ist es die Pflicht des Servers, dem Client zu sagen, warum er die Sache, die er angefordert hat, nicht bekommen konnte, also wird, um das auf der Serverseite zu erreichen, Code geschrieben, um die Ausnahme mit werfen Schlüsselwort, anstatt es zu verschlucken oder zu behandeln. Wenn der Server es behandelt/verschluckt, gibt es keine Möglichkeit, dem Client mitzuteilen, welcher Fehler aufgetreten ist.

Hinweis: Um eine klare Beschreibung des aufgetretenen Fehlertyps zu geben, können wir unser eigenes Exception-Objekt erstellen und es an den Client weitergeben.

3voto

tokhi Punkte 19702
  • Java unterscheidet zwischen zwei Kategorien von Ausnahmen (geprüfte und ungeprüfte).
  • Java erzwingt für geprüfte Ausnahmen eine catch- oder declared-Anforderung.
  • Der Typ einer Ausnahme bestimmt, ob eine Ausnahme geprüft oder nicht geprüft wird.
  • Alle Ausnahmetypen, die direkt oder indirekt sind subclasses der Klasse RuntimeException sind ungeprüfte Ausnahmen.
  • Alle Klassen, die von der Klasse Exception aber nicht RuntimeException gelten als checked exceptions .
  • Klassen, die von der Klasse Error erben, gelten als ungeprüft.
  • Der Compiler prüft jeden Methodenaufruf und jede Verzögerung, um festzustellen, ob die Methode auslöst checked exception .
    • In diesem Fall sorgt der Compiler dafür, dass die Ausnahme abgefangen oder in einer throws-Klausel deklariert wird.
  • Um den declare-Teil der catch-or-declare-Anforderung zu erfüllen, muss die Methode, die die die Ausnahme erzeugt, eine throws Klausel, die die checked-exception .
  • Exception Klassen werden so definiert, dass sie geprüft werden, wenn sie als wichtig genug erachtet werden, um sie zu fangen oder zu deklarieren.

3voto

Blueriver Punkte 2905

Ich möchte nur einige Argumente dafür anführen, dass geprüfte Ausnahmen überhaupt nicht verwendet werden sollten. Dies ist keine vollständige Antwort, aber ich denke, sie beantwortet einen Teil Ihrer Frage und ergänzt viele andere Antworten.

Immer wenn es um geprüfte Ausnahmen geht, gibt es eine throws CheckedException irgendwo in einer Methodensignatur ( CheckedException kann jede geprüfte Ausnahme sein). Eine Signatur löst KEINE Exception aus, das Auslösen von Exceptions ist ein Aspekt der Implementierung. Schnittstellen, Methodensignaturen, Elternklassen, all diese Dinge sollten NICHT von ihren Implementierungen abhängen. Die Verwendung von geprüften Exceptions hier (eigentlich die Tatsache, dass man die throws in der Methodensignatur) ist die Verknüpfung Ihrer übergeordneten Schnittstellen mit Ihren Implementierungen dieser Schnittstellen.

Ich möchte Ihnen ein Beispiel geben.

Wir wollen eine schöne und saubere Schnittstelle wie diese haben

public interface IFoo {
    public void foo();
}

Jetzt können wir viele Implementierungen der Methode foo() , wie diese

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

Die Klasse Foo ist völlig in Ordnung. Lassen Sie uns nun einen ersten Versuch mit der Klasse Bar unternehmen

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

Diese Klasse Bar lässt sich nicht kompilieren. Da InterruptedException eine geprüfte Ausnahme ist, müssen Sie sie entweder abfangen (mit einem try-catch innerhalb der Methode foo()) oder erklären, dass Sie sie auslösen (indem Sie throws InterruptedException zur Signatur der Methode). Da ich diese Ausnahme hier nicht abfangen möchte (sie soll sich nach oben ausbreiten, damit ich sie an anderer Stelle richtig behandeln kann), ändern wir die Signatur.

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

Auch diese Klasse Bar lässt sich nicht kompilieren! Die Methode foo() von Bar überschreibt NICHT die Methode foo() von IFoo, da ihre Signaturen unterschiedlich sind. Ich könnte die @Override-Annotation entfernen, aber ich möchte gegen die Schnittstelle IFoo programmieren wie IFoo foo; und später entscheiden, welche Implementierung ich verwenden möchte, z. B. foo = new Bar(); . Wenn die Methode foo() von Bar die Methode foo von IFoo nicht überschreibt, wird foo.foo(); wird die Implementierung von foo() von Bar nicht aufgerufen.

Um Bar's zu machen public void foo() throws InterruptedException IFoo's überschreiben public void foo() Ich MUSS hinzufügen throws InterruptedException zur Signatur der Methode IFoo. Dies führt jedoch zu Problemen mit meiner Klasse Foo, da sich die Signatur der Methode foo() von der Signatur der Methode IFoo unterscheidet. Außerdem, wenn ich hinzufügen throws InterruptedException zu Foo's Methode foo() würde ich eine weitere Fehlermeldung erhalten, die besagt, dass Foo's Methode foo() deklariert, dass sie eine InterruptedException auslöst, aber nie eine InterruptedException auslöst.

Wie Sie sehen können (wenn ich das alles anständig erklärt habe), zwingt mich die Tatsache, dass ich eine geprüfte Ausnahme wie InterruptedException auslöse, dazu, meine Schnittstelle IFoo an eine ihrer Implementierungen zu binden, was wiederum Chaos in den anderen Implementierungen von IFoo verursacht!

Dies ist ein wichtiger Grund, warum geprüfte Ausnahmen SCHLECHT sind. In Großbuchstaben.

Eine Lösung besteht darin, die geprüfte Ausnahme zu erfassen, sie in eine ungeprüfte Ausnahme zu verpacken und die ungeprüfte Ausnahme zu werfen.

2voto

rghome Punkte 7901

Hier ist eine einfache Regel, die Ihnen bei der Entscheidung helfen kann. Sie bezieht sich darauf, wie Schnittstellen in Java verwendet werden.

Nehmen Sie Ihre Klasse und stellen Sie sich vor, dass Sie eine Schnittstelle für sie entwerfen, die die Funktionalität der Klasse beschreibt, aber nichts von der zugrundeliegenden Implementierung (wie es eine Schnittstelle tun sollte). Stellen Sie sich vor, dass Sie die Klasse auf andere Weise implementieren könnten.

Betrachten Sie die Methoden der Schnittstelle und überlegen Sie, welche Ausnahmen sie auslösen könnten:

Wenn eine Ausnahme von einer Methode ausgelöst werden kann, unabhängig von der zugrundeliegenden Implementierung (mit anderen Worten, sie beschreibt nur die Funktionalität), dann sollte sie wahrscheinlich eine kontrollierte Ausnahme in der Schnittstelle sein.

Wenn eine Ausnahme durch die zugrunde liegende Implementierung verursacht wird, sollte sie nicht in der Schnittstelle enthalten sein. Daher muss sie entweder eine ungeprüfte Ausnahme in Ihrer Klasse sein (da ungeprüfte Ausnahmen nicht in der Schnittstellensignatur erscheinen müssen), oder Sie müssen sie verpacken und als geprüfte Ausnahme, die Teil der Schnittstellenmethode ist, erneut auslösen.

Um zu entscheiden, ob Sie die Ausnahme einpacken und erneut auslösen sollten, sollten Sie wiederum abwägen, ob es für einen Benutzer der Schnittstelle sinnvoll ist, die Ausnahmebedingung sofort zu behandeln, oder ob die Ausnahme so allgemein ist, dass Sie nichts dagegen tun können und sie sich auf dem Stack nach oben fortpflanzen sollte. Macht die umhüllte Ausnahme Sinn, wenn sie als Funktionalität der neuen Schnittstelle, die Sie definieren, ausgedrückt wird, oder ist sie nur ein Träger für eine Reihe möglicher Fehlerbedingungen, die auch bei anderen Methoden auftreten können? Wenn ersteres der Fall ist, könnte es immer noch eine geprüfte Ausnahme sein, andernfalls sollte sie ungeprüft sein.

In der Regel sollten Sie nicht planen, Ausnahmen "aufzublasen" (aufzufangen und erneut zu werfen). Entweder sollte eine Ausnahme vom Aufrufer behandelt werden (in diesem Fall wird sie geprüft) oder sie sollte den ganzen Weg bis zu einem übergeordneten Handler gehen (in diesem Fall ist es am einfachsten, wenn sie nicht geprüft wird).

2voto

Zoran Pandovski Punkte 1988

Ich möchte nur darauf hinweisen, dass Sie, wenn Sie in einem Code eine geprüfte Ausnahme auslösen und die Catch-Methode einige Ebenen darüber liegt, die Ausnahme in der Signatur jeder Methode zwischen Ihnen und der Catch-Methode deklarieren müssen. Die Kapselung ist also gebrochen, da alle Funktionen im Pfad von throw die Details dieser Ausnahme kennen müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X