Zum Hauptinhalt springen

Ausnahmen (Exceptions)

Programmfehler (Bugs) führen dazu, dass Programme unerwartete Ergebnisse liefern oder abstürzen. Je komplexer das Programm, desto wichtiger ist eine strukturierte Fehlerbehandlung. Es gibt drei grundlegende Fehlerarten: Kompilierungsfehler, Logikfehler und Laufzeitfehler.

Kompilierungsfehler verhindern, dass das Programm übersetzt und ausgeführt werden kann. Da sie bereits beim Schreiben des Codes auftreten und von Entwicklungsumgebungen direkt angezeigt werden, lassen sie sich in der Regel leicht beheben.

Die Klassenhierarchie der Laufzeitfehler

Throwable ist die Oberklasse aller Laufzeitfehler. Schwerwiegende JVM-Fehler sind Unterklassen von Error. Geprüfte Ausnahmen sind Unterklassen von Exception, ungeprüfte Ausnahmen Unterklassen von RuntimeException.

Eine Übersicht wichtiger Ausnahmenklassen findet sich in der Java API.

Definition von Ausnahmenklassen

Eigene Ausnahmenklassen werden durch Ableitung von einer bestehenden Ausnahmenklasse definiert. Es sollte immer von Exception oder einer ihrer Unterklassen abgeleitet werden — nicht von Error.

InvalidValueException.java
public class InvalidValueException extends Exception {

public InvalidValueException() {}

public InvalidValueException(String message) {}

}

Auslösen von Ausnahmen

Mit dem Schlüsselwort throw wird eine Ausnahme ausgelöst. Die auslösende Methode muss mit throws deklarieren, welche geprüften Ausnahmen sie werfen kann.

Computer.java (Auszug)
public abstract class Computer {
...
public Computer(String description, Cpu cpu, int memoryInGb) throws InvalidValueException {
if (memoryInGb <= 0) {
throw new InvalidValueException();
}
this.description = description;
this.cpu = cpu;
this.memoryInGb = memoryInGb;
}
...
}

Weiterleiten von Ausnahmen

Ausnahmen können an den Aufrufer weitergeleitet werden, anstatt sie direkt zu behandeln. Die weiterleitende Methode deklariert dafür ebenfalls throws mit der entsprechenden Ausnahmenklasse.

Notebook.java (Auszug)
public final class Notebook extends Computer implements Comparable<Notebook> {
...
public Notebook(String description, Cpu cpu, int memoryInGb, double screenSizeInInches)
throws InvalidValueException {
super(description, cpu, memoryInGb);
this.screenSizeInInches = screenSizeInInches;
}
...
}

Abfangen von Ausnahmen

Die try-catch-Anweisung überwacht einen Codeblock auf Ausnahmen. Der try-Block enthält den zu überwachenden Code, der catch-Block die Fehlerbehandlung für eine bestimmte Ausnahmenklasse.

MainClass.java
public class MainClass {

public static void main(String[] args) {
try {
Notebook notebook = new Notebook("Mein Gaming Laptop", new Cpu(4.7, 8), 32, 16);
} catch (InvalidValueException e) {
System.err.println(e.getMessage());
}
}

}

Der finally-Block

Der optionale finally-Block wird nach dem try- und allen catch-Blöcken immer ausgeführt — unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht. Er eignet sich daher für Aufräumarbeiten wie das Schließen von Ressourcen.

MainClass.java
public class MainClass {

public static void main(String[] args) {
try {
Notebook notebook = new Notebook("Mein Gaming Laptop", new Cpu(4.7, 8), 32, 16);
} catch (InvalidValueException e) {
System.err.println(e.getMessage());
} finally {
System.out.println("wird immer ausgeführt");
}
}

}

Werden Ressourcen wie Dateiströme geöffnet, empfiehlt sich statt finally die Try-with-Resources-Anweisung. Klassen, die AutoCloseable implementieren, werden dabei am Ende des try-Blocks automatisch geschlossen — auch im Fehlerfall.

MainClass.java
public class MainClass {

public static void main(String[] args) {
try (FileReader reader = new FileReader("data.txt")) {
// Datei lesen
} catch (IOException e) {
System.err.println(e.getMessage());
}
}

}