1. Arten von Ausnahmen

Arten von Ausnahmen

Alle Ausnahmen sind in 4 Typen unterteilt, bei denen es sich tatsächlich um Klassen handelt, die sich gegenseitig erben.

ThrowableKlasse

Die Basisklasse für alle Ausnahmen ist die ThrowableKlasse. Die ThrowableKlasse 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 ThrowableKlasse abgeleitet ist. Und obwohl man theoretisch Code wie schreiben kann throw new Throwable();, macht das normalerweise niemand. Der Hauptzweck der ThrowableKlasse besteht darin, für alle Ausnahmen eine einzige übergeordnete Klasse zu haben.

ErrorKlasse

Die nächste Ausnahmeklasse ist die ErrorKlasse, die die Klasse direkt erbt Throwable. Die Java-Maschine erstellt Objekte der ErrorKlasse (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 Errorausgelö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.

ExceptionKlasse

Die Klassen Exceptionund RuntimeExceptionsind 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, catchder 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.

RuntimeExceptionKlasse

RuntimeExceptionssind eine Teilmenge von Exceptions. Wir könnten sogar sagen, dass RuntimeExceptiones sich um eine vereinfachte Version gewöhnlicher Ausnahmen handelt ( Exception) – für solche Ausnahmen gelten weniger Anforderungen und Einschränkungen

ExceptionDen Unterschied zwischen und erfahren Sie RuntimeExceptionspäter.


2. Throws: geprüfte Ausnahmen

Würfe: geprüfte Ausnahmen

Alle Java-Ausnahmen fallen in zwei Kategorien: aktiviert und nicht aktiviert .

Alle Ausnahmen, die das RuntimeExceptionoder erben, Errorgelten als ungeprüfte Ausnahmen . Alle anderen sind geprüfte Ausnahmen .

Wichtig!

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 throwsSchlüsselwort an (verwenden Sie das Schlüsselwort nicht throwversehentlich). Es sieht ungefähr so ​​aus:

type method (parameters) throws exception

Beispiel:

geprüfte Ausnahme ungeprüfte Ausnahme
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

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 throwswird 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 throwsdurch 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 catchfür jede Ausnahme Blöcke hinzufügen, oder indem Sie sie einer throwsKlausel 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
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
Die Methode löst möglicherweise zwei geprüfte Ausnahmen aus:

  • EmptyWorldException
  • LonelyWorldException

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
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Die aufrufende Methode fängt die Ausnahmen nicht ab und muss andere darüber informieren: Sie fügt sie ihrer eigenen throwsKlausel 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
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
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 throwsSchlü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
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
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 throwsKlausel aller Methoden, die diese Methoden aufrufen. Und von den Methoden, die diese Methoden aufrufen.

Dadurch throwserhalten 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
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Rufen Sie die im RuntimeExceptionObjekt gespeicherte Ausnahme ab. Die causeVariable 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 tryfolgen .catch

Oder es könnten 3 catchBlöcke mit demselben Code und weitere 2 catchBlö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 catchBlock 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, catchwie Sie möchten. Ein einzelner Block kann jedoch catchkeine Ausnahmen angeben, die sich gegenseitig erben. Mit anderen Worten, Sie können kein Catch ( Exception| RuntimeExceptione) schreiben, da die RuntimeExceptionKlasse erbt Exception.



5. Benutzerdefinierte Ausnahmen

Sie können jederzeit Ihre eigene Ausnahmeklasse erstellen. Sie erstellen einfach eine Klasse, die die RuntimeExceptionKlasse 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
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Werfen Sie eine ungeprüfte MyException .

In der Java-Multithreading- Quest werden wir uns eingehend mit der Arbeit mit unseren eigenen benutzerdefinierten Ausnahmen befassen.