10.3 "Checked" und "Unchecked Exceptions"

Die Klasse Throwable

Die Klasse Throwable ist die Basisklasse der gesamten Ausnahmehierarchie. Instanzen von ihr sorgen dafür das Ausnahmen "geworfen" werden, die dann behandelt werden können.

Alle Klassen in der Throwable Hierarchie können den Grund für das Werfen einer Ausnahme im Konstruktor als Zeichenkette beschreiben.

Die Klasse Error

Die Klasse Error wird für Probleme der Java virtuellen Maschine selbst verwendet. Diese Fehler sollten nicht auftreten. Fehler die durch diese Klasse gemeldet werden sind in der Regel so schwerwiegend, dass keine weitere Fortführung des Programms sinnvoll ist.

Ein Beispiel hierfür ist das Fehlen von freiem Hauptspeicher. Er wird mit einem OutOfMemoryError Fehler gemeldet. Eine sinnvolle Behandlung dieses Fehlers ist nur schwer möglich. Selbst ein Berichten über die Details dieses Problem benötigt Speicherplatz zum Verwalten von Zeichenketten. Der Versuch einer Behandlung eines solchen Fehlers ist nur sinnvoll wenn man in der Lage ist, in nativen Bibliotheken die die Anwendung selbst verwaltet unmittelbar auf C/C++ Ebene Speicher freizugeben.

Da alle Fehler in dieser Klassenhierarchie (Errror) nicht direkt von der entsprechenden Sektion im Programm abhängen und sehr selten sind, zählen diese Fehler zu den "unchecked Exceptions". Ein Entwickler muss Codestrecken nicht mit einem try-catch Block versehen und eine Behandlung implementieren.

Wichtig

Alle abgeleiteten Klassen der Klasse Error sind "unchecked exceptions".

Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden.

Hintergrund: Fehler in dieser Kategorie können in jeder Java-Codestrecke auftreten. Der Entwickler müsste seinen gesamten Code mit try-catch Blöcken versehen. Hinzukommt, dass eine Behandlung in der Regel nicht möglich ist. Try-catch Blöcke sind allso nicht sinnvoll für diese Kategerie von Ausnahmen.

Beispiele
Oberklasse Error Bedeutung
InstantiationError Versuch des Erzeugens einer Instanz einer abstrakten Klasse oder einer Schnittstelle
StackOverflowError Überlauf des Stapel (Stack). Typische Ursachen sind Programm mit extrem vielen und tiefen Methodenaufrufen oder ein nicht terminierendes rekurvsives Programm
OutOfMemoryError Der Javaprozess hat keinen freien Hauptspeicher mehr und kann keine weiteren Objekte allokieren

 

Die Klasse Exception

Aus der Klasse Exception werden alle Ausnahmen abgeleitet, die ein Entwickler zur Laufzeit behandeln kann. Alle Klassen in dieser Unterhierarchie sind "Checked Exceptions" mit Ausnahme der Klasse RuntimeException. Dies bedeutet Sie müssen mit try-catch Blöcken behandelt werden.

Die Klasse RuntimeException

In der Hierarchie der Klasse RuntimeException sind alle Ausnahmen zu finden die im "normalen" Betrieb des Laufzeitsystems auftreten können. Sie sind auch "unchecked Exceptions" die man nicht im Code behandeln muss, da sie jederzeit auftreten können. 

Wichtig

Alle abgeleiteten Klassen der Klasse RuntimeException sind "unchecked exceptions".

Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden.

 

Einige Ausnahmeklassen aus dieser Hierarchie sind:

Beispiele
Oberklasse RuntimeException Bedeutung
ArithmeticException Division durch Null bei Ganzzahlwerten.
NegativeArraySizeException Versuch der Erzeugung eines Feldes mit einer negativen Größe.
NullPointerException Versuchter Zugriff auf eine Instanz deren Referenz mit null belegt ist.

 

Das Konzept von "Checked" und "Unchecked Exceptions"

Die Unterscheidung dieser beiden Arten von Ausnahmen hat für den Entwickler die Konsequenz, dass er bei Aufrufen von Methoden die zu "Checked Exceptions" führen, eine entsprechende Behandlung bei der Implementierung durchführen muss. Behandlung bedeutet. "Unchecked Exceptions" müssen nicht in der Implementierung berücksichtig und behandelt werden.

Checked Exception

Bei Aufrufen von Methoden die zu "Checked Exceptions" führen können hat der Entwickler zwei Möglichkeiten:

  • Aufruf der entsprechen Methode innerhalb eines try-catch Blocks und der entsprechenden Behandlung der Ausnahme 
  • Weiterreichen der Ausnahme indem die gesamte Methode das Schlüsselwort throws und die Ausnahme benutzt um die Behandlung der Ausnahme an den aufrufenden Block weiterreicht.

Wird eine "checked Exception nicht mit einem try-catch Block behandelt so besteht auch die Möglichkeit die Ausnahme nicht zu behandeln. In diesem Fall muss die Ausnahme aus der aktuellen Methode herausgereicht werden. Hierfür muss die Methode im Kopf der entsprechenden Methode mit Hilfe des Schlüsselwort throws nach Aussen weitergereicht werden. Die Syntax des Methodenkopfs ist:

qualifier [return-parameter] methoden-name (parameter-liste) {throws eine-ausnahme} {...}

Ein konkreter Methodenkopf ist z. Bsp.

public boolean istKleinerAls (Object s) throws NachnameExceptionRT {...} 

Beispiel: Die Klasse BufferReader.readLine() wird benötigt um Konsoleneingaben zu Lesen.

Unchecked Exception

"Unchecked Exception" sind sehr vielfältig und sie können in fast jeder Zeile Code auftreten.  Diese Ausnahmen müssen nicht zwingend mit try-catch Blöcken abgefangen werden.

Der Entwickler muss bei "Unchecked Exceptions" selbst die Entscheidung treffen welche Codestrecken das Potential haben eine Ausnahme auszulösen.

Das Entwurfsziel besteht darin sicher zu codieren und den Code noch lesbar zu gestalten.

Beispiel:

Eine potentielle Division durch Null kann an sehr vielen Stellen auftreten. Müsste man jede Division durch Null mit einem try-catch Block ummanteln wäre das sehr aufwendig und der Code wird sehr schlecht lesbar sein.

In der folgenden Tabelle können in den ersten beiden Fällen keine Divisionen durch Null auftreten. Im dritten Fall kann eine solche Division abhängig vom Eingabewert c auftreten. Hier obliegt es dem Entwickler das Risiko einer Division durch Null abzuschätzen und die Division eventuell mit einem try-catch Block zu schützen.

Fall 1: kein Risiko Fall 2: kein Risiko Fall 3: Risiko einer Ausnahme
public int test(int a) {
   int b = a / 2;
   return b;
}
public int test(int a) {
   int c = 2;
   int b = a / c;
   return b;
}
public int test(int a, int c) {
   int b = a / c;
   return b;
}