1. Arten von Ausnahmen
Alle Ausnahmen sind in 4 Typen unterteilt, bei denen es sich tatsächlich um Klassen handelt, die sich gegenseitig erben.
Throwable
Klasse
Die Basisklasse für alle Ausnahmen ist die Throwable
Klasse. Die Throwable
Klasse enthält den Code, der den aktuellen Aufrufstapel (Stacktrace der aktuellen Methode) in ein Array schreibt. Was ein Stacktrace ist, erfahren wir etwas später.
Der Throw- Operator kann nur ein Objekt akzeptieren, das von der Throwable
Klasse abgeleitet ist. Und obwohl man theoretisch Code wie schreiben kann throw new Throwable();
, macht das normalerweise niemand. Der Hauptzweck der Throwable
Klasse besteht darin, für alle Ausnahmen eine einzige übergeordnete Klasse zu haben.
Error
Klasse
Die nächste Ausnahmeklasse ist die Error
Klasse, die die Klasse direkt erbt Throwable
. Die Java-Maschine erstellt Objekte der Error
Klasse (und ihrer Nachkommen) , wenn schwerwiegende Probleme aufgetreten sind . Zum Beispiel eine Hardwarestörung, unzureichender Speicher usw.
Normalerweise können Sie als Programmierer nichts tun , wenn ein solcher Fehler (die Art, für die ein Error
ausgelöst werden sollte) im Programm aufgetreten ist: Diese Fehler sind zu schwerwiegend. Sie können den Benutzer lediglich darüber informieren, dass das Programm abstürzt, und/oder alle bekannten Informationen über den Fehler in das Programmprotokoll schreiben.
Exception
Klasse
Die Klassen Exception
und RuntimeException
sind für häufige Fehler vorgesehen, die bei der Ausführung vieler Methoden auftreten. Das Ziel jeder ausgelösten Ausnahme besteht darin, von einem Block abgefangen zu werden, catch
der weiß, wie er richtig damit umgeht.
Wenn eine Methode aus irgendeinem Grund ihre Arbeit nicht abschließen kann, sollte sie die aufrufende Methode sofort benachrichtigen, indem sie eine Ausnahme des entsprechenden Typs auslöst.
Mit anderen Worten: Wenn eine Variable gleich ist null
, löst die Methode einen aus NullPointerException
. Wenn der Methode die falschen Argumente übergeben wurden, wird eine ausgelöst InvalidArgumentException
. Wenn die Methode versehentlich durch Null dividiert, wird ein Fehler ausgegeben ArithmeticException
.
RuntimeException
Klasse
RuntimeExceptions
sind eine Teilmenge von Exceptions
. Wir könnten sogar sagen, dass RuntimeException
es sich um eine vereinfachte Version gewöhnlicher Ausnahmen handelt ( Exception
) – für solche Ausnahmen gelten weniger Anforderungen und Einschränkungen
Exception
Den Unterschied zwischen und erfahren Sie RuntimeException
später.
2. Throws
: geprüfte Ausnahmen
Alle Java-Ausnahmen fallen in zwei Kategorien: aktiviert und nicht aktiviert .
Alle Ausnahmen, die das RuntimeException
oder erben, Error
gelten als ungeprüfte Ausnahmen . Alle anderen sind geprüfte Ausnahmen .
Zwanzig Jahre nach der Einführung geprüfter Ausnahmen hält fast jeder Java-Programmierer dies für einen Fehler. In gängigen modernen Frameworks sind 95 % aller Ausnahmen ungeprüft. Die C#-Sprache, die Java fast exakt kopiert hat, hat keine geprüften Ausnahmen hinzugefügt .
Was ist der Hauptunterschied zwischen aktivierten und nicht aktivierten Ausnahmen?
Für geprüfte Ausnahmen gelten zusätzliche Anforderungen . Grob gesagt sind es diese:
Anforderung 1
Wenn eine Methode eine geprüfte Ausnahme auslöst , muss sie in ihrer Signatur den Typ der Ausnahme angeben . Auf diese Weise weiß jede Methode, die sie aufruft, dass diese „bedeutungsvolle Ausnahme“ darin auftreten könnte.
Geben Sie geprüfte Ausnahmen nach den Methodenparametern nach dem throws
Schlüsselwort an (verwenden Sie das Schlüsselwort nicht throw
versehentlich). Es sieht ungefähr so aus:
type method (parameters) throws exception
Beispiel:
geprüfte Ausnahme | ungeprüfte Ausnahme |
---|---|
|
|
Im Beispiel rechts löst unser Code eine ungeprüfte Ausnahme aus – es sind keine weiteren Maßnahmen erforderlich. Im Beispiel links löst die Methode eine geprüfte Ausnahme aus, daher throws
wird das Schlüsselwort zusammen mit dem Typ der Ausnahme zur Methodensignatur hinzugefügt.
Wenn eine Methode erwartet, mehrere geprüfte Ausnahmen auszulösen , müssen alle throws
durch Kommas getrennt nach dem Schlüsselwort angegeben werden. Die Reihenfolge ist nicht wichtig. Beispiel:
public void calculate(int n) throws Exception, IOException
{
if (n == 0)
throw new Exception("n is null!");
if (n == 1)
throw new IOException("n is 1");
}
Anforderung 2
Wenn Sie eine Methode aufrufen, deren Signatur geprüfte Ausnahmen enthält, können Sie die Tatsache, dass sie diese auslöst, nicht ignorieren.
Sie müssen alle derartigen Ausnahmen entweder abfangen, indem Sie catch
für jede Ausnahme Blöcke hinzufügen, oder indem Sie sie einer throws
Klausel für Ihre Methode hinzufügen.
Es ist, als würden wir sagen: „ Diese Ausnahmen sind so wichtig, dass wir sie abfangen müssen.“ Und wenn wir nicht wissen, wie wir damit umgehen sollen, muss jeder, der unsere Methode aufrufen könnte, darüber informiert werden, dass solche Ausnahmen darin auftreten können.
Beispiel:
Stellen Sie sich vor, wir schreiben eine Methode, um eine von Menschen bevölkerte Welt zu schaffen. Als Argument wird die anfängliche Personenzahl übergeben. Daher müssen wir Ausnahmen hinzufügen, wenn zu wenige Personen anwesend sind.
Die Erde erschaffen | Notiz |
---|---|
|
Die Methode löst möglicherweise zwei geprüfte Ausnahmen aus:
|
Dieser Methodenaufruf kann auf drei Arten abgewickelt werden:
1. Fangen Sie keine Ausnahmen ein
Dies geschieht am häufigsten, wenn die Methode nicht weiß, wie sie mit der Situation richtig umgehen soll.
Code | Notiz |
---|---|
|
Die aufrufende Methode fängt die Ausnahmen nicht ab und muss andere darüber informieren: Sie fügt sie ihrer eigenen throws Klausel hinzu |
2. Fangen Sie einige Ausnahmen ab
Wir behandeln die Fehler, mit denen wir umgehen können. Aber diejenigen, die wir nicht verstehen, werfen wir der aufrufenden Methode vor. Dazu müssen wir ihren Namen zur throws-Klausel hinzufügen:
Code | Notiz |
---|---|
|
Der Aufrufer fängt nur eine geprüfte Ausnahme ab – LonelyWorldException . Die andere Ausnahme muss zu ihrer Signatur hinzugefügt werden, indem sie nach dem throws Schlüsselwort angegeben wird |
3. Fangen Sie alle Ausnahmen ab
Wenn die Methode keine Ausnahmen für die aufrufende Methode auslöst, ist die aufrufende Methode immer sicher, dass alles gut funktioniert hat. Und es wird nicht in der Lage sein, Maßnahmen zur Behebung einer Ausnahmesituation zu ergreifen.
Code | Notiz |
---|---|
|
Alle Ausnahmen werden in dieser Methode abgefangen. Der Anrufer wird sicher sein, dass alles gut gelaufen ist. |
3. Ausnahmen umschließen
Geprüfte Ausnahmen schienen in der Theorie cool zu sein, erwiesen sich in der Praxis jedoch als große Frustration.
Angenommen, Sie haben in Ihrem Projekt eine sehr beliebte Methode. Es wird von Hunderten von Stellen in Ihrem Programm aufgerufen. Und Sie beschließen, eine neue geprüfte Ausnahme hinzuzufügen. Und es kann durchaus sein, dass diese geprüfte Ausnahme wirklich wichtig und so speziell ist, dass nur die main()
Methode weiß, was zu tun ist, wenn sie abgefangen wird.
Das bedeutet, dass Sie die geprüfte Ausnahme zur Klausel jeder Methode hinzufügen müssen , die Ihre besonders beliebte Methode aufruftthrows
. Sowie in der throws
Klausel aller Methoden, die diese Methoden aufrufen. Und von den Methoden, die diese Methoden aufrufen.
Dadurch throws
erhalten die Klauseln der Hälfte der Methoden im Projekt eine neue geprüfte Ausnahme. Und natürlich wird Ihr Projekt durch Tests abgedeckt, und jetzt werden die Tests nicht kompiliert. Und jetzt müssen Sie auch die Throws-Klauseln in Ihren Tests bearbeiten.
Und dann muss Ihr gesamter Code (alle Änderungen in Hunderten von Dateien) von anderen Programmierern überprüft werden. Und an diesem Punkt fragen wir uns, warum wir so viele blutige Änderungen an dem Projekt vorgenommen haben? Arbeitstage und fehlerhafte Tests – alles nur, um eine geprüfte Ausnahme hinzuzufügen?
Und natürlich gibt es immer noch Probleme im Zusammenhang mit der Vererbung und dem Überschreiben von Methoden. Die Probleme, die durch geprüfte Ausnahmen entstehen, sind viel größer als der Nutzen. Das Fazit ist, dass sie heutzutage nur noch wenige Menschen lieben und nur wenige Menschen sie nutzen.
Allerdings gibt es immer noch viel Code (einschließlich Standard-Java-Bibliothekscode), der diese geprüften Ausnahmen enthält. Was ist mit ihnen zu tun? Wir können sie nicht ignorieren und wissen nicht, wie wir mit ihnen umgehen sollen.
Java-Programmierer schlugen vor, geprüfte Ausnahmen in . einzuschließen RuntimeException
. Mit anderen Worten: Fangen Sie alle geprüften Ausnahmen ab, erstellen Sie dann ungeprüfte Ausnahmen (z. B. RuntimeException
) und lösen Sie sie stattdessen aus. Das sieht in etwa so aus:
try
{
// Code where a checked exception might occur
}
catch(Exception exp)
{
throw new RuntimeException(exp);
}
Es ist keine sehr schöne Lösung, aber hier gibt es nichts Kriminelles: Die Ausnahme wurde einfach in eine RuntimeException
.
Bei Bedarf können Sie es dort ganz einfach abrufen. Beispiel:
Code | Notiz |
---|---|
|
Rufen Sie die im RuntimeException Objekt gespeicherte Ausnahme ab. Die cause Variable kann ihren Typ bestimmen und ihn in einen geprüften Ausnahmetyp null umwandeln . |
4. Mehrere Ausnahmen abfangen
Programmierer hassen es wirklich, Code zu duplizieren. Sie haben sich sogar ein entsprechendes Entwicklungsprinzip ausgedacht: Don't Repeat Yourself (DRY) . Bei der Behandlung von Ausnahmen kommt es jedoch häufig vor, dass auf einen Block mehrere Blöcke mit demselben Code try
folgen .catch
Oder es könnten 3 catch
Blöcke mit demselben Code und weitere 2 catch
Blöcke mit einem anderen identischen Code vorhanden sein. Dies ist eine Standardsituation, wenn Ihr Projekt verantwortungsvoll mit Ausnahmen umgeht.
Ab Version 7 wurde der Java-Sprache die Möglichkeit hinzugefügt, mehrere Ausnahmetypen in einem einzigen catch
Block anzugeben. Es sieht ungefähr so aus:
try
{
// Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
// Exception handling code
}
Sie können so viele Blöcke haben, catch
wie Sie möchten. Ein einzelner Block kann jedoch catch
keine Ausnahmen angeben, die sich gegenseitig erben. Mit anderen Worten, Sie können kein Catch ( Exception
| RuntimeException
e) schreiben, da die RuntimeException
Klasse erbt Exception
.
5. Benutzerdefinierte Ausnahmen
Sie können jederzeit Ihre eigene Ausnahmeklasse erstellen. Sie erstellen einfach eine Klasse, die die RuntimeException
Klasse erbt. Es wird ungefähr so aussehen:
class ClassName extends RuntimeException
{
}
Wir besprechen die Details, während Sie OOP, Vererbung, Konstruktoren und Methodenüberschreibung lernen.
Aber selbst wenn Sie nur eine einfache Klasse wie diese haben (ganz ohne Code), können Sie dennoch darauf basierende Ausnahmen auslösen:
Code | Notiz |
---|---|
|
Werfen Sie eine ungeprüfte MyException . |
In der Java-Multithreading- Quest werden wir uns eingehend mit der Arbeit mit unseren eigenen benutzerdefinierten Ausnahmen befassen.
GO TO FULL VERSION