TL; DR
Prämisse
- Laufzeitausnahmen sollten ausgelöst werden, wenn der Fehler nicht behebbar ist: Wenn der Fehler im Code enthalten ist und nicht vom externen Status abhängt (daher würde die Wiederherstellung den Code korrigieren).
- Überprüfte Ausnahmen sollten ausgelöst werden, wenn der Code korrekt ist, der externe Status jedoch nicht den Erwartungen entspricht: Keine Netzwerkverbindung, Datei nicht gefunden oder beschädigt usw.
Fazit
Wir können eine aktivierte Ausnahme als Laufzeitausnahme erneut auslösen, wenn der Propagierungs- oder Schnittstellencode davon ausgeht, dass die zugrunde liegende Implementierung vom externen Status abhängt, wenn dies eindeutig nicht der Fall ist.
In diesem Abschnitt wird erläutert, wann eine der Ausnahmen ausgelöst werden soll. Sie können zur nächsten horizontalen Leiste springen, wenn Sie nur eine ausführlichere Erklärung für die Schlussfolgerung lesen möchten.
Wann ist es angebracht, eine Laufzeitausnahme auszulösen? Sie lösen eine Laufzeitausnahme aus, wenn klar ist, dass der Code nicht korrekt ist und die Wiederherstellung durch Ändern des Codes angemessen ist.
Beispielsweise ist es angebracht, eine Laufzeitausnahme für Folgendes auszulösen:
float nan = 1/0;
Dadurch wird eine Laufzeitausnahme für die Division durch Null ausgelöst. Dies ist angemessen, da der Code fehlerhaft ist.
Oder zum Beispiel, hier ist ein Teil von HashMap
's Konstruktor:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
Um die anfängliche Kapazität oder den anfänglichen Auslastungsfaktor zu beheben, müssen Sie den Code bearbeiten, um sicherzustellen, dass die richtigen Werte übergeben werden. Dies hängt nicht davon ab, ob ein weit entfernter Server in Betrieb ist. eine Datei oder ein anderes Programm. Der Konstruktor, der mit ungültigen Argumenten aufgerufen wird, hängt von der Richtigkeit des aufrufenden Codes ab, sei es eine falsche Berechnung, die zu ungültigen Parametern oder einem unangemessenen Ablauf führte, bei dem ein Fehler übersehen wurde.
Wann ist es angebracht, eine aktivierte Ausnahme auszulösen? Sie werfen eine geprüfte Ausnahme , wenn das Problem ist ohne Änderung des Codes erstattungsfähig. Anders ausgedrückt, Sie lösen eine aktivierte Ausnahme aus, wenn der Fehler mit dem Status zusammenhängt, während der Code korrekt ist.
Jetzt kann das Wort "Wiederherstellen" hier schwierig sein. Dies kann bedeuten, dass Sie einen anderen Weg finden, um das Ziel zu erreichen: Wenn der Server beispielsweise nicht antwortet, sollten Sie den nächsten Server versuchen. Wenn diese Art der Wiederherstellung für Ihren Fall möglich ist, ist das großartig, aber das ist nicht das einzige, was Wiederherstellung bedeutet - Wiederherstellung könnte einfach darin bestehen, dem Benutzer einen Fehlerdialog anzuzeigen, der erklärt, was passiert ist, oder wenn es sich um eine Serveranwendung handelt, könnte dies der Fall sein Senden Sie eine E-Mail an den Administrator, oder protokollieren Sie den Fehler lediglich angemessen und präzise.
Nehmen wir das Beispiel, das in der Antwort von mrmuggles erwähnt wurde:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
Dies ist nicht der richtige Weg, um die aktivierte Ausnahme zu behandeln. Die bloße Unfähigkeit, die Ausnahme im Geltungsbereich dieser Methode zu behandeln, bedeutet nicht, dass die App abstürzen sollte. Stattdessen ist es angebracht, es in einem höheren Bereich wie folgt zu verbreiten:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Was die Möglichkeit der Wiederherstellung durch den Anrufer ermöglicht:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
Überprüfte Ausnahmen sind ein statisches Analysetool. Sie machen einem Programmierer klar, was bei einem bestimmten Aufruf schief gehen kann, ohne dass er die Implementierung erlernen oder einen Test- und Fehlerprozess durchlaufen muss. Auf diese Weise kann auf einfache Weise sichergestellt werden, dass keine Teile des Fehlerflusses ignoriert werden. Das erneute Auslösen einer aktivierten Ausnahme als Laufzeitausnahme wirkt sich auf diese arbeitssparende statische Analysefunktion aus.
Es ist auch erwähnenswert, dass die aufrufende Schicht einen besseren Kontext für das größere Schema der Dinge aufweist, wie oben gezeigt wurde. Es könnte viele Ursachen dafür geben, dataAccessCode
dass angerufen werden würde, der konkrete Grund für den Anruf ist nur für den Anrufer sichtbar - somit ist es in der Lage, eine bessere Entscheidung bei der korrekten Wiederherstellung nach einem Ausfall zu treffen.
Nachdem wir diese Unterscheidung getroffen haben, können wir ableiten, wann es in Ordnung ist, eine aktivierte Ausnahme erneut als Laufzeitausnahme zu werfen.
Wann ist es in Anbetracht des oben Gesagten angebracht, eine aktivierte Ausnahme erneut als RuntimeException auszugeben? Wenn der von Ihnen verwendete Code vom externen Status abhängig ist, können Sie jedoch eindeutig behaupten, dass er nicht vom externen Status abhängt.
Folgendes berücksichtigen:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
In diesem Beispiel wird der Code weitergegeben, IOException
da die API von Reader
für den Zugriff auf den externen Status ausgelegt ist. Wir wissen jedoch, dass die StringReader
Implementierung nicht auf den externen Status zugreift. In diesem Bereich, in dem wir mit Sicherheit behaupten können, dass die an dem Aufruf beteiligten Teile nicht auf E / A oder einen anderen externen Zustand zugreifen, können wir die Ausnahme sicher als Laufzeitausnahme zurückwerfen, ohne Kollegen zu überraschen, die sich unserer Implementierung nicht bewusst sind (und dies möglicherweise tun) unter der Annahme, dass der IO-Zugangscode einen IOException
) auslöst .
Der Grund für die strikte Überprüfung von externen zustandsabhängigen Ausnahmen ist, dass sie nicht deterministisch sind (im Gegensatz zu logikabhängigen Ausnahmen, die vorhersehbar jedes Mal für eine Version des Codes reproduziert werden). Wenn Sie beispielsweise versuchen, durch 0 zu dividieren, wird immer eine Ausnahme erzeugt. Wenn Sie nicht durch 0 dividieren, wird niemals eine Ausnahme erzeugt, und Sie müssen diesen Ausnahmefall nicht behandeln, da dies niemals passieren wird. Wenn Sie jedoch einmal auf eine Datei zugreifen, bedeutet dies nicht, dass Sie das nächste Mal erfolgreich sind - der Benutzer hat möglicherweise die Berechtigungen geändert, ein anderer Prozess hat sie möglicherweise gelöscht oder geändert. Sie müssen also immer mit diesem Ausnahmefall fertig werden, oder Sie haben wahrscheinlich einen Fehler.