Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 12 - Exceptions

## 12.2 Behandlung von Exceptions

- 12.2 Behandlung von Exceptions
  - 12.2.1 Die try-catch-Anweisung
  - 12.2.2 Das Fehlerobjekt
  - 12.2.3 Die Fehlerklassen von Java
  - 12.2.4 Fortfahren nach Fehlern
  - 12.2.5 Mehr als eine catch-Klausel
  - 12.2.6 Die finally-Klausel

### 12.2.1 Die try-catch-Anweisung

Das Behandeln von Ausnahmen erfolgt mit Hilfe der `try`-`catch`-Anweisung:

    001 try {
    002   Anweisung;
    003   ...
    004 } catch (Ausnahmetyp x) {
    005   Anweisung;
    006   ...
    007 }

Listing 12.1: Die try-catch-Anweisung

Der `try`-Block enthält dabei eine oder mehrere Anweisungen, bei deren Ausführung ein Fehler des Typs `Ausnahmetyp` auftreten kann. In diesem Fall wird die normale Programmausführung unterbrochen, und der Programmablauf fährt mit der ersten Anweisung nach der `catch`-Klausel fort, die den passenden Ausnahmetyp deklariert hat. Hier kann nun Code untergebracht werden, der eine angemessene Reaktion auf den Fehler realisiert.

Wir wollen das folgende fehlerhafte Programm betrachten:

    001 /* RTErrorProg1.java */
    002
    003 public class RTErrorProg1
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, base = 0;
    008
    009     for (base = 10; base >= 2; --base) {
    010       i = Integer.parseInt("40",base);
    011       System.out.println("40 base "+base+" = "+i);
    012     }
    013   }
    014 }
| RTErrorProg1.java

Listing 12.2: Ein Programm mit einem Laufzeitfehler

Hier soll der String "40" aus verschiedenen Zahlensystemen in ein `int` konvertiert und als Dezimalzahl ausgegeben werden. Die dazu verwendete Methode `parseInt` überprüft, ob der übergebene String einen gültigen Zahlenwert zur angegebenen Basis darstellt. Ist dies nicht der Fall, löst sie eine Ausnahme des Typs `NumberFormatException` aus. Ohne weitere Maßnahmen stürzt das Programm dann beim Versuch, den String "40" als Zahl zur Basis 4 anzusehen, ab:

    40 base 10 = 40
    40 base 9 = 36
    40 base 8 = 32
    40 base 7 = 28
    40 base 6 = 24
    40 base 5 = 20
    Exception in thread "main" java.lang.NumberFormatException: 40
            at java.lang.Integer.parseInt(Compiled Code)
            at RTErrorProg1.main(Compiled Code)

Das Programm läßt sich nun leicht gegen solche Fehler absichern, indem die Programmsequenz, die den Fehler verursacht, in eine `try`-`catch`-Anweisung eingeschlossen wird:

    001 /* Listing1203.java */
    002
    003 public class Listing1203
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, base = 0;
    008
    009     try {
    010       for (base = 10; base >= 2; --base) {
    011         i = Integer.parseInt("40",base);
    012         System.out.println("40 base "+base+" = "+i);
    013       }
    014     } catch (NumberFormatException e) {
    015       System.out.println(
    016         "40 ist keine Zahl zur Basis "+base
    017       );
    018     }
    019   }
    020 }
| Listing1203.java

Listing 12.3: Abfangen des Laufzeitfehlers mit einer try-catch-Anweisung

Zwar ist 40 immer noch keine Zahl zur Basis 4, aber das Programm fängt diesen Fehler nun ab und gibt eine geeignete Fehlermeldung aus:

    40 base 10 = 40
    40 base 9 = 36
    40 base 8 = 32
    40 base 7 = 28
    40 base 6 = 24
    40 base 5 = 20
    40 ist keine Zahl zur Basis 4

### 12.2.2 Das Fehlerobjekt

In der `catch`-Klausel wird nicht nur die Art des abzufangenden Fehlers definiert, sondern auch ein formaler Parameter angegeben, der beim Auftreten der Ausnahme ein Fehlerobjekt übernehmen soll. Fehlerobjekte sind dabei Instanzen der Klasse `Throwable` oder einer ihrer Unterklassen. Sie werden vom Aufrufer der Ausnahme erzeugt und als Parameter an die `catch`-Klausel übergeben. Das Fehlerobjekt enthält Informationen über die Art des aufgetretenen Fehlers. So liefert beispielsweise die Methode `getMessage` einen Fehlertext (wenn dieser explizit gesetzt wurde), und `printStackTrace` druckt einen Auszug aus dem Laufzeit-Stack. Am einfachsten kann der Fehlertext mit der Methode `toString` der Klasse `Throwable` ausgegeben werden:

    public String getMessage()

    public void printStackTrace()

    public String toString()
| java.lang.Throwable

Unser Beispielprogramm könnte dann wie folgt umgeschrieben werden:

    001 /* RTErrorProg2.java */
    002
    003 public class RTErrorProg2
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, base = 0;
    008
    009     try {
    010       for (base = 10; base >= 2; --base) {
    011         i = Integer.parseInt("40",base);
    012         System.out.println("40 base "+base+" = "+i);
    013       }
    014     } catch (NumberFormatException e) {
    015       System.out.println("***Fehler aufgetreten***");
    016       System.out.println("Ursache: "+e.getMessage());
    017       e.printStackTrace();
    018     }
    019   }
    020 }
| RTErrorProg2.java

Listing 12.4: Verwendung des Fehlerobjekts nach einem Laufzeitfehler

Die Ausgabe des Programms wäre dann:

    40 base 10 = 40
    40 base 9 = 36
    40 base 8 = 32
    40 base 7 = 28
    40 base 6 = 24
    40 base 5 = 20
    ***Fehler aufgetreten***
    Ursache: 40
    java.lang.NumberFormatException: 40
            at java.lang.Integer.parseInt(Compiled Code)
            at RTErrorProg2.main(Compiled Code)

Wie man sieht, ähnelt die Ausgabe des Programms der ersten Version, die ohne expliziten Fehler-Handler geschrieben wurde. Das liegt daran, daß das Java-Laufzeitsystem beim Auftreten eines Fehlers, der von keiner Methode behandelt wurde, `printStackTrace` aufruft, bevor es das Programm beendet.

### 12.2.3 Die Fehlerklassen von Java

Alle Laufzeitfehler in Java sind Unterklassen der Klasse `Throwable`. `Throwable` ist eine allgemeine Fehlerklasse, die im wesentlichen eine Klartext-Fehlermeldung speichern und einen Auszug des Laufzeit-Stacks ausgeben kann. Unterhalb von `Throwable` befinden sich zwei große Vererbungshierarchien:

- Die Klasse `Error` ist Superklasse aller schwerwiegenden Fehler. Diese werden hauptsächlich durch Probleme in der virtuellen Java-Maschine ausgelöst. Fehler der Klasse `Error` sollten in der Regel nicht abgefangen werden, sondern (durch den Standard-Fehlerhandler) nach einer entsprechenden Meldung zum Abbruch des Programms führen.
- Alle Fehler, die möglicherweise für die Anwendung selbst von Interesse sind, befinden sich in der Klasse `Exception` oder einer ihrer Unterklassen. Ein Fehler dieser Art signalisiert einen abnormen Zustand, der vom Programm abgefangen und behandelt werden kann.

Viele Pakete der Java-Klassenbibliothek definieren ihre eigenen Fehlerklassen. So gibt es spezielle Fehlerklassen für die Dateiein- und -ausgabe, die Netzwerkkommunikation oder den Zugriff auf Arrays. Wir werden diese speziellen Fehlerklassen immer dann erläutern, wenn eine Methode besprochen wird, die diese Art von Fehler erzeugt.

### 12.2.4 Fortfahren nach Fehlern

Die Reaktion auf eine Ausnahme muß keinesfalls zwangsläufig darin bestehen, das Programm zu beenden. Statt dessen kann auch versucht werden, den Fehler zu beheben oder zu umgehen, um dann mit dem Programm fortzufahren. Wird im obigen Programm die `try`-`catch`-Anweisung in die Schleife gesetzt, so fährt das Programm nach jedem Fehler fort und versucht, die Konvertierung zur nächsten Basis vorzunehmen:

    001 /* Listing1205.java */
    002
    003 public class Listing1205
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, base = 0;
    008
    009     for (base = 10; base >= 2; --base) {
    010       try {
    011         i = Integer.parseInt("40",base);
    012         System.out.println("40 base "+base+" = "+i);
    013       } catch (NumberFormatException e) {
    014         System.out.println(
    015           "40 ist keine Zahl zur Basis "+base
    016         );
    017       }
    018     }
    019   }
    020 }
| Listing1205.java

Listing 12.5: Fortfahren nach Laufzeitfehlern

Die Ausgabe des Programms lautet nun:

    40 base 10 = 40
    40 base 9 = 36
    40 base 8 = 32
    40 base 7 = 28
    40 base 6 = 24
    40 base 5 = 20
    40 ist keine Zahl zur Basis 4
    40 ist keine Zahl zur Basis 3
    40 ist keine Zahl zur Basis 2

|  | Ob es sinnvoller ist, nach einem Fehler mit der Programmausführung fortzufahren oder das Programm abzubrechen, ist von der Art des Fehlers und der Stelle im Programm, an der er aufgetreten ist, abhängig. Hier muß von Fall zu Fall entschieden werden, wie vorgegangen werden soll. |  |
Hinweis

### 12.2.5 Mehr als eine catch-Klausel

Bisher sind wir davon ausgegangen, daß innerhalb eines `try`-Blocks nur eine Ausnahme auftreten kann. Tatsächlich ist es natürlich ohne weiteres möglich, daß zwei oder mehrere unterschiedliche Ausnahmen ausgelöst werden. Das Programm kann auf verschiedene Fehler reagieren, indem es mehr als eine `catch`-Klausel verwendet. Jede `catch`-Klausel fängt die Fehler ab, die zum Typ des angegebenen Fehlerobjekts zuweisungskompatibel sind. Dazu gehören alle Fehler der angegebenen Klasse und all ihrer Unterklassen (das wichtige Konzept der Zuweisungskompatibilität von Objekttypen wurde in Abschnitt 7.1.6 erläutert). Die einzelnen `catch`-Klauseln werden in der Reihenfolge ihres Auftretens abgearbeitet.

Wir wollen das Beispielprogramm nun so erweitern, daß nicht nur ein einzelner String in eine Zahl konvertiert wird, sondern ein Array von Strings. Aufgrund eines Fehlers in der verwendeten `for`-Schleife verursacht das Programm einen Zugriff auf ein nicht vorhandenes Array-Element und löst damit eine Ausnahme des Typs `IndexOutOfBoundsException` aus. Diese kann zusammen mit der Ausnahme `NumberFormatException` in einer gemeinsamen `try`-`catch`-Anweisung behandelt werden:

    001 /* Listing1206.java */
    002
    003 public class Listing1206
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, j, base = 0;
    008     String[] numbers = new String[3];
    009
    010     numbers[0] = "10";
    011     numbers[1] = "20";
    012     numbers[2] = "30";
    013     try {
    014       for (base = 10; base >= 2; --base) {
    015         for (j = 0; j <= 3; ++j) {
    016           i = Integer.parseInt(numbers[j],base);
    017           System.out.println(
    018             numbers[j]+" base "+base+" = "+i
    019           );
    020         }
    021       }
    022     } catch (IndexOutOfBoundsException e1) {
    023       System.out.println(
    024         "***IndexOutOfBoundsException: " + e1.toString()
    025       );
    026     } catch (NumberFormatException e2) {
    027       System.out.println(
    028         "***NumberFormatException: " + e2.toString()
    029       );
    030     }
    031   }
    032 }
| Listing1206.java

Listing 12.6: Mehr als eine catch-Klausel

Die Ausgabe des Programms ist nun:

    10 base 10 = 10
    20 base 10 = 20
    30 base 10 = 30
    ***IndexOutOfBoundsException: java.lang.ArrayIndexOutOfBoundsException

### 12.2.6 Die finally-Klausel

Die `try`-`catch`-Anweisung enthält einen optionalen Bestandteil, der bisher noch nicht erläutert wurde. Mit Hilfe der `finally`-Klausel, die als letzter Bestandteil einer `try`-`catch`-Anweisung verwendet werden darf, kann ein Programmfragment definiert werden, das immer dann ausgeführt wird, wenn die zugehörige `try`-Klausel betreten wurde. Dabei spielt es keine Rolle, welches Ereignis dafür verantwortlich war, daß die `try`-Klausel verlassen wurde. Die `finally`-Klausel wird insbesondere dann ausgeführt, wenn der `try`-Block durch eine der folgenden Anweisungen verlassen wurde:

- wenn das normale Ende des `try`-Blocks erreicht wurde
- wenn eine Ausnahme aufgetreten ist, die durch eine `catch`-Klausel behandelt wurde
- wenn eine Ausnahme aufgetreten ist, die nicht durch eine `catch`-Klausel behandelt wurde
- wenn der `try`-Block durch eine der Sprunganweisungen `break`, `continue` oder `return` verlassen werden soll

|  | Die `finally`-Klausel ist also der ideale Ort, um Aufräumarbeiten durchzuführen. Hier können beispielsweise Dateien geschlossen oder Ressourcen freigegeben werden. |  |
Hinweis

Die folgende Variation unseres Beispielprogramms benutzt `finally` dazu, am Ende des Programms eine Meldung auszugeben:

    001 /* Listing1207.java */
    002
    003 public class Listing1207
    004 {
    005   public static void main(String[] args)
    006   {
    007     int i, base = 0;
    008
    009     try {
    010       for (base = 10; base >= 2; --base) {
    011         i = Integer.parseInt("40",base);
    012         System.out.println("40 base "+base+" = "+i);
    013       }
    014     } catch (NumberFormatException e) {
    015       System.out.println(
    016         "40 ist keine Zahl zur Basis "+base
    017       );
    018     } finally {
    019       System.out.println(
    020         "Sie haben ein einfaches Beispiel " +
    021         "sehr glücklich gemacht."
    022       );
    023     }
    024   }
    025 }
| Listing1207.java

Listing 12.7: Verwendung der finally-Klausel

Die Ausgabe des Programms ist:

    40 base 10 = 40
    40 base 9 = 36
    40 base 8 = 32
    40 base 7 = 28
    40 base 6 = 24
    40 base 5 = 20
    40 ist keine Zahl zur Basis 4
    Sie haben ein einfaches Beispiel sehr glücklich gemacht.

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de