Exception

Einführung

Exception bedeutet Ausnahme oder Ausnahmesituation. Eine Exception ist ein schwerwiegender Fehler, der ohne Behandlung zu Abstürzen oder unerwünschten Programmzuständen führt. Exceptions treten immer erst zur Programmausführung auf. Dies passiert unter anderem bei arithmetischen Operationen (Division durch 0), Verletzung von Arraygrenzen, Datentypkonvertierung oder bei Zugriff auf noch nicht erzeugte Objekte.

Für die Behandlung solcher Fehler gibt es in fortgeschrittenen Programmiersprachen ein Konzept, mit dem Programme unter fast allen Umständen sicher weiterlaufen und nicht abstürzen.

Tritt ein definierter Fehlerzustand ein, kann dieser Abgefangen und behandelt bzw. "geheilt" werden. Die Fehlerkategorie wird durch den Namen der Exception charakterisiert. So definiert die Standardbibliothek eine ArithmeticException für mathematische Fehler oder eine IOException für Fehler im Zusammenhang mit Ein-/Ausgabeoperationen.

Folgende Porgammiersprachen verfügen über das Konzept der Exceptions (http://de.wikipedia.org/wiki/Ausnahmebehandlung):

  • Object Pascal

  • C++

  • C#

  • ISO Modula-2

  • Visual Basic .NET

  • Java

  • JavaScript

  • PHP

  • Python

  • Perl

Das Exception Objekt

Alle Errors und Exceptions sind in Klassen definiert, die von Throwable abstammen. Dies ermöglicht es auch eigene Exceptions zu definieren, indem einfach eine Unterklasse von einer vorhandenen Exception erbt.

Quelle (http://www.javamex.com/tutorials/exceptions/ExceptionHierarchy.png)

Von den Exceptions sind die Errors zu unterscheiden. Während die Exceptions vergleichsweise einfache Fehler signalisieren, zeigen Errors schwerwiegende, nicht behebbare Fehler an, nach deren Auftreten die Fortsetzung des Programms nicht sinnvoll ist. Sie führen zum Abbruch des Programms.

Der Ablauf

Der Umgang mit Exceptions erfolgt in Java mit der try/catch-Anweisung. Beim Auftreten einer Exception wird zunächst festgestellt, ob sie in der Methode in der sie aufgetreten ist, durch eine try/catch-Anweisung abgefangen wird. Falls die Exception in der gesamten Methode nicht abgefangen wird, wird sie an die Methode weitergeleitet, die in der dynamischen Aufrufhierarchie der Methoden die nächsthöhere ist. Dort wird wieder im Programmblock des Aufrufs nachgeschaut, ob die Exception abgefangen wird.

So wandert eine Exception die Aufrufhierarchie hinauf, bis sie abgefangen wird. Falls sie nirgends abgefangen wird, bricht das Programm die Ausführung ab.

Die Behandlung

Es gibt drei Varianten zur Behandlung von Exceptions:

  • Die Methode, die eine Exception auslösen kann, wird in eine try-Anweisung eingefasst (mit optionalen catch-Anweisungen). Hierdurch erfolgt eine Fehlerbehandlung »vor Ort«.

  • Es wird auf eine lokale Fehlerbehandlung verzichtet. Alternativ muss im Kopf der betreffenden Methode eine throws-Klausel aufgeführt werden. Durch eine throws-Klausel zeigt eine Methode an, dass sie eine bestimmte Exception nicht selbst behandelt, sondern diese an die aufrufende Methode weiterleitet.

  • Als Zwischenlösung kann eine Exception abgefangen und stattdessen eine andere ausgelöst werden, die in der throws-Klausel angegeben ist.

Falls weder eine lokale Fehlerbehandlung erfolgt noch eine throws-Klausel verwendet wird, liefert der Compiler eine entsprechende Fehlermeldung. Eine Ausnahme von dieser Regelung ist die RuntimeException und ihre Unterklassen. Für sie ist weder die lokale Behandlung mit catch noch die Angabe von throws zwingend erforderlich.

Das Abfangen

Unter dem Abfangen einer Exception versteht man die Maßnahmen, die ergriffen werden, damit das Programm sinnvoll und definiert weiterlaufen kann.

Zum Abfangen einer Exception und dem Reagieren auf ihr Auftreten gibt es eigens die Schlüsselwörter try, catch und finally. Der allgemeine Ablauf des Abfangens einer Exception hat folgende Struktur:

try { // Anweisungsblock, der eine Exception auslösen kann }

catch (ErsteException e) { // Anweisungen, die beim Auftreten einer // ErsteException ausgeführt werden }

finally { // Abschlussaktionen, die grundsätzlich // ausgeführt werden }

try-Block

Die Ausnahmebehandlung beginnt mit dem try-Block. Try bedeutet Versuch. Das Programm versucht also die Anweisung die im Körper des try-Blockes steht abzuarbeiten. Geht etwas schief und sollte dort eine Exception "geworfen" werden, so wird diese in den catch-Block übergeben. In der Zeile, in der die Exception verursacht wurde, wird direkt der try-Block verlassen. Die danach folgenden Anweisungen des try-Blockes werden nicht mehr abgearbeitet!

Der try-Block wird syntaktisch durch das Schlüsselwort try eingeleitet. Dann folgt ein geschweiftes Klammerpaar {}. Dieses Klammerpaar entspricht dem Körper des try-Blocks. Hier werden alle auszuführenden Anweisungen hineingeschrieben.

catch-Block

Der catch-Block ist vergleichbar mit einer Methode, der im Falle eines Fehlers eine Exception übergeben wird. Durch mehrere catch-Blöcke kann die Fehlerbehandlung differenziert festgelegt werden. Es gibt eine Vielzahl unterschiedlicher Exception-Klassen. Je nach Klasse kann eine andere Fehlerbehandlung erfolgen.

Sollte es einen catch-Block geben, der die Super-Klasse Exception abfängt, werden alle Unterarten von Exception gemäß der Vererbungshierarchie ebenfalls mit behandelt.

Im catch-Block werden Methoden zur Fehlerausgabe ausgeführt, die dem Benutzer dann angezeigt werden können und grundsätzlich verständlich für den Adressaten sein sollten.

Es können beliebig viele catch-Blöcke hinter dem try-Block vorhanden sein, wenn Sie jeweils eine andere Exception behandeln.

Ein catch-Block wird syntaktisch durch das Schlüsselwort catch eingeleitet. Dann folgt ein geschweiftes Klammerpaar {}. Dieses Klammerpaar entspricht dem Körper des catch-Blocks. Hier werden alle auszuführenden Anweisungen hineingeschrieben.

finally-Block

Als letztes folgt der finally-Block. Dieser ist optional und wird immer ausgeführt, wenn er vorhanden ist. Hier werden Aktionen definiert, die zur Fehlerbehandlung durchgeführt werden. Wenn ein finally Block vorhanden ist, wird er auf jeden Fall abgearbeitet, egal, ob ein catch-Block angeschlagen hat oder nicht.

Der finally-Block wird syntaktisch durch das Schlüsselwort finally eingeleitet. Dann folgt ein geschweiftes Klammerpaar {}. Dieses Klammerpaar entspricht dem Körper des finally-Blocks. Hier werden alle auszuführenden Anweisungen hineingeschrieben.

Weiterleitung

Mit der throws-Klausel im Kopf der Methode signalisiert eine Methode, dass sie eine Exception nicht lokal durch try-catch abfängt, sondern die Fehlerbehandlung dem Aufrufer überlässt. Die throws-Klausel wird folgendermaßen verwendet:

FileInputStream stream;

public void openFile(String name) throws FileNotFoundException {

stream = new FileInputStream(name);

}

Der Konstruktor FileInputStream wirft die Exception FileNotFoundException. Diese Exception muss abgefangen oder weitergereicht werden. In diesem Beispiel wird sie von der aufrufenden Methode openFile weitergereicht, in dem im Kopf der Methode openFile throws FileNotFoundException implementiert wird. Tritt der Fehler tatsächlich ein, muss er in einer aufrufenden Methode behandelt werden. Entweder wird dort der die Exception mittels try-catch abgefangen oder erneut an eine andere aufrufende Methode mittels throws weitergereicht.

Ein Beispiel

Das folgende Beispiel demonstriert die Anwendung von Exceptions. Mittels eines Scanners werden Zeichenketten durch einer User-Eingabe eingelesen und mit der next()-Methode zurückgeben. Im try-Block wird die Methode parseStringToInt aufgerufen. Diese Methode wurde mittels throws implementiert. Im Kopf wird festgelegt, dass sie eine NumberFormatException schmeißen kann.

Gibt der User keine Zahl als String ein (z.B. "9"), kann dieser String nicht vom Konstruktor Integer konvertiert werden. Eine NumberFormatException wird geschmissen, weitergereicht und catch-Block von der frageNummerAb gefangen.

import java.util.*;// Diese Bibliothek stellt die Scanner Klasse bereit

public class Eingabe

{

private Scanner scanner = new Scanner(System.in);

int nr=0;

public int frageNummerAb(){

Scanner scanner = new Scanner(System.in);

System.out.println("Bitte geben Sie eine Zahl ein!");

try{

nr=parseStringToInt(scanner.next());// Verwendet die Methode parseStringToInt um einen Eingabe-String in einen Integer zu konvertieren. Als Eingabe erhält sie einen String vom Scanner Objekt

return nr;

}catch(NumberFormatException e){// Fängt die Exception NumberFormatException ab, die im try-Block von der Methode parseStringToInt geworfen wurde

// Hier wird verständlich über den Fehler informiert und eine erneute Eingabe aufgerufen.

System.out.println("Ihre Eingabe war fehlerhaft!");

System.out.println("Bitte wiederholen Sie Ihre Eingabe!");

frageNummerAb();

}

return nr;

}

// Methode die einen String in eine Zahl konvertiert

private int parseStringToInt(String input)throws NumberFormatException{

return new Integer(input).intValue();

}

}