MarktundTechnik Home-Page Previous Page TOC Index Next Page See Page



17. Tag

Ausnahmen

von Charles L. Perkins

Sie lernen heute etwas über außergewöhnliche Bedingungen in Java:

Wir beginnen mit den Grundlagen, warum es überhaupt Möglichkeiten zur Handhabung von Ausnahmen gibt und wozu sie dienen.

An Programmiersprachen wurde lange gebastelt, um folgendes häufiges Problem zu lösen:

int status = callSomethingThatAlmostAlwaysWorks();

if (status == FUNNY_RETURN_VALUE) {

   ... // Etwas Ungewöhnliches ist passiert und muß gehandhabt werden.

   switch(someGlobalErrorIndicator) {

      ... // Handhabung spezifischer Probleme

   }

} else {

   ... // Alles in Ordnung, Sie können sich entspannen.

}

Irgendwie sieht das nach großem Aufwand aus, um einen seltenen Fall zu handhaben. Wenn die aufgerufene Funktion int als Teil ihrer normalen Antwort zurückgibt, müssen Sie eine spezielle Ganzzahl (FUNNY_RETURN_VALUE) herausstellen, um auf einen Fehler hinzuweisen. Was aber, wenn diese Funktion wirklich alle Ganzzahlen braucht? Dann müssen Sie noch härter vorgehen.

Auch wenn es Ihnen gelingt, einen bestimmten Wert zu finden (z. B. NULL in C für Pointer, -1 für Ganzzahlen usw.), stellt sich die Frage, was passiert, wenn mehrere Fehler von der gleichen Funktion erzeugt werden müssen. Meist wird eine globale Variable als Fehlerindikator benutzt. Die Funktion speichert einen Wert darin und hofft, daß er von niemandem geändert wird, bis der Rufende den Fehler zur Handhabung erhält. Mehrere Fehler werden - wenn überhaupt - schlecht verteilt, was sich bei größeren Programmen, komplexen Fehlern usw. noch erschwert.

Zum Glück gibt es eine Alternative: Mit Ausnahmen können außergewöhnliche Bedingungen in einem Programm gehandhabt werden, so daß der normale Code sauberer und übersichtlicher wird.

Eine Ausnahme ist ein Objekt, das eine Instanz der Klasse Throwable (oder einer ihrer Subklassen) ist.

Programmieren im Großen

Mit zunehmender Erfahrung im Java-Programmieren werden Sie feststellen, daß Sie nach dem Design der Klassen und Schnittstellen sowie der Methodenbeschreibungen immer noch keine Eigenschaften für Ihre Objekte definiert haben. Schließlich beschreibt eine Schnittstelle die übliche Verwendungsweise eines Objekts, beinhaltet aber keine seltsamen Ausnahmefälle. Bei vielen Systemen wird dieses Problem in der Dokumentation gelöst, z. B. durch Auflisten von herausstellbaren Werten, wie im vorherigen Beispiel. Da dem System darüber nichts bekannt ist, kann es keine Konsistenzprüfung durchführen. Der Compiler kann bei solchen Ausnahmebedingungen in keiner Weise eingreifen, im Gegensatz zu den hilfreichen Warnungen und Fehlermeldungen, die er produziert, wenn eine Methode fehlerhaft ist.

Dieser Aspekt wird im Programmdesign nicht berücksichtigt. Vielmehr sind Sie gezwungen, das irgendwie in der Dokumentation zu beschreiben in der Hoffnung, daß keiner später bei der Implementierung einen Fehler macht. Das wird dadurch noch erschwert, daß jeder die gleiche Sache anders beschreibt. Sie benötigen also eine einheitliche Form der Deklaration der Absichten von Klassen und Methoden in bezug auf diese Ausnahmebedingungen. Java bietet eine solche Möglichkeit:

public class MyFirstExceptionalClass {

   public void anExceptionalMethod() throws MyFirstException {

   ...

   }

}

Hier wird der Leser (und der Compiler) darauf aufmerksam gemacht, daß der Code ... eine Ausnahme namens MyFirstException auswerfen kann.

Sie können sich die Beschreibung einer Methode als Vertrag zwischen dem Designer und der Methode (oder Klasse) und sich selbst als Aufrufer der Methode vorstellen. Normalerweise teilt diese Beschreibung die Typen der Argumente einer Methode, was sie ausgibt, und die allgemeine Semantik mit. Ihnen wird ebenfalls mitgeteilt, welche abnormen Dinge sie ausführen kann. Das ist ein Versprechen, genauso wie die Methode verspricht, einen Wert eines bestimmten Typs auszugeben, und Sie sich darauf verlassen können, wenn Sie den Code schreiben. Diese neuen Versprechen helfen, alle Stellen, an denen Ausnahmebedingungen in Ihrem Programm gehandhabt werden sollen, explizit zu bezeichnen.

Da Ausnahmen Instanzen von Klassen sind, können sie in eine Hierarchie gestellt werden, die auf natürliche Weise die Beziehungen zwischen den verschiedenen Ausnahmearten beschreibt. Wenn Sie sich die Diagramme von java.lang-Fehlern und java.lang-Ausnahmen in Anhang B ansehen, werden Sie feststellen, daß unter der Klasse Throwable zwei große Klassenhierarchien stehen. Die Wurzeln dieser zwei Hierarchien sind Subklassen von Throwable namens Exception und Error. Diese Hierarchien verkörpern die reichhaltigen Beziehungen, die zwischen Ausnahmen und Fehlern in der Java-Laufzeitumgebung bestehen.

Wenn Sie wissen, daß eine bestimmte Fehler- oder Ausnahmenart in Ihrer Methode auftreten kann, müssen Sie diese entweder selbst handhaben oder die potentiellen Aufrufer explizit mit der throws-Klausel darauf aufmerksam machen. Sie müssen nicht alle Fehler und Ausnahmen auflisten. Instanzen der Klasse Error oder RuntimeException (eine ihrer Subklassen) müssen in der throws-Klausel nicht aufgeführt werden. Sie werden besonders behandelt, weil sie irgendwo in einem Java-Programm auftreten können und normalerweise durch Bedingungen verursacht werden, die nicht auf den Programmierer zurückzuführen sind. Ein gutes Beispiel dafür ist OutOfMemoryError, ein Fehler, der jederzeit aus vielen Gründen auftreten kann.

Sie können diese Fehler und Laufzeitausnahmen auf Wunsch selbstverständlich auflisten, dann sind die Aufrufer Ihrer Methoden gezwungen, sie zu handhaben. Wenn das Wort »Ausnahme« irgendwo allein steht, bedeutet es fast immer »Ausnahme oder Fehler« (d. h. eine Throwable-Instanz). In der obigen Diskussion wurde erklärt, daß Ausnahmen und Fehler im Grunde zwei getrennte Hierarchien bilden, sich abgesehen von der throws-Regel aber gleich verhalten.

Wenn Sie sich die Diagramme in Anhang B genauer ansehen, werden Sie feststellen, daß es nur fünf Ausnahmearten (in java.lang) gibt, die in einer throws-Klausel aufgelistet werden müssen (bedenken Sie, daß alle Errors und RuntimeExceptions Ausnahmen sind):

Jeder dieser Namen deutet etwas an, das explizit vom Programmierer veranlaßt wird, nicht etwas, das hinter den Kulissen abläuft, wie etwa OutOfMemoryError.

Weiter unten in den Diagrammen von java.util und java.io in Anhang B sehen Sie, daß jedes Paket neue Ausnahmen hinzufügt. java.util fügt die zwei Ausnahmen ArrayStoreException und IndexOutOfBoundsException hinzu und stellt sie unter RuntimeException. java.io fügt einen ganzen Baum von IOExceptions hinzu, die eher vom Programmierer verursacht werden und deshalb unter der Wurzel Exception eingereiht werden. Somit muß IOExceptions in throws-Klauseln beschrieben werden. Schließlich definiert das Paket java.awt jeweils einen impliziten und einen expliziten Stil.

Die Java-Klassenbibliothek nutzt Ausnahmen überall sehr wirkungsvoll. Wenn Sie sich die ausführliche API-Dokumentation Ihres Java-Releases ansehen, sehen Sie, daß viele Methoden in der Bibliothek throws-Klauseln haben. Einige davon sind sogar dokumentiert (um sie dem Leser klarer darzustellen). Das ist lediglich auf die Nettigkeit des Verfassers zurückzuführen, denn von Ihnen wird nicht erwartet, in Ihren Programmen derartige Bedingungen zu berücksichtigen.

Programmieren im Kleinen

Sie haben inzwischen schon ein gutes Gefühl bekommen, auf welche Weise Ausnahmen das Design eines Programms und einer Klassenbibliothek verbessern können. Wie aber werden Ausnahmen praktisch angewandt? Wir wollen das nun mit anExceptionalMethod() aus dem ersten Beispiel der heutigen Lektion versuchen:

public void anotherExceptionalMethod() throws MyFirstException {

   MyFirstExceptionalClass aMFEC = new MyFirstExceptionalClass();

   aMFEC.anExceptionalMethod();

}

Wir betrachten dieses Beispiel jetzt genauer. Da MyFirstException eine Subklasse von Exception ist, müssen Sie sie im Code von anotherExceptionalMethod() handhaben oder andernfalls die Aufrufer entsprechend warnen. Da Ihr Code anExceptionalMethod() lediglich aufruft, ohne die Tatsache zu berücksichtigen, daß MyFirstException ausgeworfen werden könnte, müssen Sie diese Ausnahme in Ihre throws-Klausel einfügen. Das ist absolut zulässig und verschont die Aufrufer vor etwas, für das Sie eigentlich zuständig sind (was selbstverständlich von den Umständen abhängt).

Nehmen wir an, Sie fühlen sich heute verantwortlich. Sie entschließen sich, die Ausnahme zu handhaben. Da Sie jetzt eine Methode ohne throws-Klausel deklarieren, müssen Sie die erwarteten Ausnahmen mit catch auffangen und etwas Nützliches damit anfangen:

public void responsibleMethod() {

   MyFirstExceptionalClass aMFEC = new MyFirstExceptionalClass();

   try {

      aMFEC.anExceptionalMethod();

   } catch (MyFirstException m) {

   ... // Tun Sie etwas schrecklich Wichtiges

   }

}

Die try-Anweisung sagt praktisch alles: »Versucht, den Code innerhalb dieser Klammern auszuführen. Falls Ausnahmen ausgeworfen werden, sind entsprechende Handler dafür verfügbar.« (Darüber haben Sie erstmals am 10. Tag etwas gehört.) Sie können am Ende von try beliebig viele catch-Klauseln einfügen. Mit jeder einzelnen können Sie die Ausnahmen, die in Instanzen vorkommen, handhaben. Mit catch in diesem Beispiel werden Ausnahmen der Klasse MyFirstException (oder einer ihrer Subklassen) gehandhabt.

Wenn Sie beide aufgezeigten Ansätze kombinieren, d. h. die Ausnahme selbst handhaben, sie aber auch den Aufrufern zur Kenntnis bringen wollen, können Sie das durch explizites Auswerfen der Ausnahme:

public void responsibleExceptionalMethod() throws MyFirstException {

   MyFirstExceptionalClass aMFEC = new MyFirstExceptionalClass();

   try {

      aMFEC.anExceptionalMethod();

   } catch (MyFirstException m) {

      ... // Etwas Verantwortungsvolles tun

      throw n; // Erneutes Auswerfen der Ausnahme

   }

}

Das funktioniert, weil Ausnahmen-Handler darin verschachtelt sind. Sie können die Ausnahme handhaben, indem Sie etwas Verantwortungsvolles mit ihr anfangen, entschließen sich aber, keinen Ausnahmen-Handler bereitzustellen, wenn die Aufrufer selbst eine Gelegenheit haben, sie zu handhaben. Ausnahmen fließen über die gesamte Kette der Methodenaufrufe (von denen die meisten normalerweise nicht gehandhabt werden), bis das System zuletzt eventuell nicht aufgefangene Ausnahmen selbst handhabt, indem es das Programm beendet und eine Fehlermeldung ausgibt. In einem Einzelprogramm ist das nicht einmal so schlecht. In einem Applet aber kann das den Browser zum Absturz bringen. Die meisten Browser schützen sich vor dieser Katastrophe, indem sie alle Ausnahmen beim Ausführen eines Applets selbst auffangen, sicher ist das aber nicht. Die Faustregel lautet deshalb: Falls es Ihnen möglich ist, eine Ausnahme aufzufangen, sollten Sie das auch tun.

Wir betrachten nun, wie das Auswerfen einer neuen Ausnahme aussieht. Wir schlachten das Beispiel der ersten Lektion weiter aus:

public class MyFirstExceptionalClass {

   public void anExceptionalMethod() throws MyFirstException {

      ...

      if (someThingUnusualHasHappened()) {

         throw new MyFirstException();

         // Die Ausführung kommt nicht bis hierher

      }

   }

}

throw ist mit der break-Anweisung vergleichbar - was danach folgt, wird nicht mehr ausgeführt.

Das ist eine grundlegende Möglichkeit, alle Ausnahmen zu erzeugen. Die ganze Hierarchie unter der Klasse Throwable wäre nichts wert, wenn man die throw-Anweisung nicht überall im Code einwerfen könnte. Da sich Ausnahmen über jede Tiefe bis in die Methoden hinein ausbreiten können, kann jeder Methodenaufruf theoretisch eine Fülle möglicher Fehler und Ausnahmen erzeugen. Zum Glück muß man nur die in der throws-Klausel der jeweiligen Methode auflisten. Der Rest wandert stillschweigend zur Ausgabe einer Fehlermeldung (oder bis er vom System aufgefangen wird).

Im folgenden ungewöhnlichen Beispiel wird das aufgezeigt, wobei throw und der auffangende Handler sehr eng zusammenliegen:

System.out.print("Now ");

try {

   System.out.print("is ");

   throw new MyFirstException();

   System.out.print("a ");

} catch (MyFirstException m) {

      System.out.print("the ");

}

System.out.print("time.");

Dieser Code gibt die Zeichenkette Now is the time. aus.

Ausnahmen sind eine starke Technik, um den Bereich aller möglichen Fehlerbedingungen in handhabbare Stücke aufzuteilen. Da die erste passende catch-Klausel ausgeführt wird, können Sie Ketten wie beispielsweise folgende erstellen:

try {

   someReallyExceptionalMethod();

} catch (NullPointerException n) { // Eine Subklasse von RuntimeException

   ...

} catch (RuntimeException r) { // Eine Subklasse von Exception

   ...

} catch (IOException i) { // Eine Subklasse von Exception

   ...

} catch (MyFirstException m) { // Unsere Subklasse von Exception

   ...

} catch (Exception e) { // Eine Subklasse von Throwable

   ...

} catch (Throwable t) {

   ... // Fehler, plus alles, was oben nicht aufgefangen wurde

}

Indem Subklassen vor ihren Elternklassen aufgelistet werden, fangen die Eltern alles auf, was normalerweise auch von einer der darüberstehenden Subklassen aufgefangen würde. Mit Ketten dieser Art können Sie fast alle Testkombinationen ausdrücken. Findet etwas wirklich Seltsames statt, das Sie nicht handhaben können, kann dies eventuell durch Verwendung einer Schnittstelle aufgefangen werden. Dadurch können Sie Ihre Ausnahmenhierarchie unter Verwendung der Mehrfachvererbung auslegen. Das Auffangen einer Schnittstelle anstatt einer Klasse eignet sich auch zum Testen einer Eigenschaft, die in vielen Ausnahmen vorkommt, in einem Einfachvererbungsbaum aber nicht ausgedrückt werden kann.

Nehmen wir beispielsweise an, daß mehrere in Ihrem Code verteilte Ausnahmenklassen nach dem Auswerfen einen Neustart voraussetzen. Sie können eine Schnittstelle namens NeedsReboot erstellen, so daß alle Ausnahmenklassen die Schnittstelle implementieren. (Keine davon braucht eine Elternklasse.) Dann fängt der Ausnahmen-Handler auf der höchsten Ebene Klassen auf, die NeedsReboot implementieren, und führt einen Neustart aus:

public interface NeedsReboot { } // Braucht überhaupt keinen Inhalt

try {

   someMethodThatGeneratesExceptionsThatImplementNeedsReboot();

} catch (NeedsReboot n) { // Schnittstelle auffangen

      ... // Aufräumen

      SystemClass.reboot(); // Neustart anhand einer erfundenen Systemklasse

}

Übrigens, wenn Sie wirklich ungewöhnliche Verhaltensweisen während einer Ausnahme brauchen, können Sie diese in die Ausnahmenklasse einfügen! Denken Sie daran, daß eine Ausnahme eine normale Klasse ist. Deshalb kann sie Instanzvariablen und Methoden enthalten. Deren Verwendung ist zwar ein bißchen seltsam, kann sich bei absonderlichen Situationen aber als nützlich erweisen. Das könnte etwa so aussehen:

try {

   someExceptionallyStrangeMethod();

} catch (ComplexException e) {

   switch (e.internalState()) { // Wahrscheinlich der Wert einer Instanzvariablen

      case e.COMPLEX_CASE: // Eine Klassenvariable der Ausnahme

         e.performComplexBehavior(myState, theContext, etc);

         break;

      ...

   }

}

Einschränkungen beim Programmieren

So leistungsstark sich das alles anhört, kann man sich des Eindrucks nicht erwehren, daß es gewisse Einschränkungen auferlegt, stimmt's? Nehmen wir beispielsweise an, Sie wollen die Standardmethode toString() der Object-Klasse übergehen, um sich einen Einblick zu verschaffen, was eigentlich alles ausgegeben wird:

public class MyIllegalClass {

   public String toString() {

      someReallyExceptionalMethod();

      ... // Gibt eine Zeichenkette aus

   }

}

Da die Superklasse Object die Methodendeklaration für toString() ohne throws-Klausel definiert, muß eine Implementierung davon in einer Subklasse dieser Einschränkung gehorchen. Insbesondere können Sie nicht einfach someReallyExceptionalMethod() wie zuvor aufrufen, weil sie eine Fülle von Fehlern und Ausnahmen erzeugt, von denen einige in der throws-Klausel aufgelistet werden (z. B. IOException und MyFirstException). Würden keine ausgeworfenen Ausnahmen in der Liste stehen, hätten Sie kein Problem. Da einige aber darin aufgeführt werden, müssen Sie mindestens diese mit catch auffangen:

public class MyLegalClass {

   public String toString() {

      try {

         someReallyExceptionalMethod();

      } catch (IOException e) {

      } catch (MyFirstException m) {

      }

      ... // Gibt eine Zeichenkette aus

   }

}

In beiden Fällen werden die Ausnahmen zwar aufgefangen, jedoch wird absolut nichts unternommen. Das ist zulässig, aber nicht immer die richtige Entscheidung. Man sollte sich einige Gedanken machen, um das beste nicht triviale Verhalten einer bestimmten catch-Klausel festzulegen. Diese zusätzliche Denkarbeit macht Ihr Programm robuster, kann ungewöhnliche Eingaben leichter handhaben und funktioniert im Zusammenhang mit Multithreading (lernen Sie morgen) besser.

Die toString()-Methode von MyIllegalClass produziert einen Kompilierfehler. Wenn Sie sich darüber einige Gedanken machen, um die Situation optimal zu lösen, werden Sie reichlich dafür belohnt, denn Ihre Klassen können dann in späteren Projekten bzw. größeren Programmen wiederverwendet werden. Selbstverständlich wurde die Java-Bibliothek mit genau dieser Sorgfalt entwickelt. Unter anderem ist sie auch deshalb robust, so daß vielseitige Java-Projekte entwickelt werden können.

Die finally-Klausel

Nehmen wir an, es gibt eine Aktion, die Sie unbedingt ausführen müssen, egal was passiert. Normalerweise ist das das Freistellen von externen Ressourcen, die beansprucht wurden, oder um eine Datei zu schließen usw. Um sicher zu gehen, daß »egal was passiert« auch Ausnahmen beinhaltet, benutzen Sie eine Klausel der try-Anweisung, die genau für diese Zwecke entwickelt wurde - finally:

SomeFileClass f = new SomeFileClass();

if (f.open("/a/file/name/path")) {

   try {

      someReallyExceptionalMethod();

   } finally {

      f.close();

   }

}

Diese Anwendung von finally verhält sich ungefährt so:

SomeFileClass f = new SomeFileClass();

if (f.open("/a/file/name/path")) {

      try {

         someReallyExceptionalMethod();

      } catch (Throwable t) {

         f.close();

         throw t;

   }

}

ausgenommen, daß finally hier auch zum Reinigen nach Ausnahmen und nach return-, break- und continue-Anweisungen benutzt werden kann. Nachfolgend eine komplexe Demonstration:

public class MyFinalExceptionalClass extends ContextClass {

   public static void main(String argv[]) {

      int mysteriousState = getContext();

      while (true) {

         System.out.print("Who ");

         try {

            System.out.print("is ");

            if (mysteriousState == 1)

               return;

            System.out.print("that ");

            if (mysteriousState == 2)

               break;

            System.out.print("strange ");

            if (mysteriousState == 3)

               continue;

            System.out.print("but kindly ");

            if (mysteriousState == 4)

               throw new UncaughtException();

            System.out.print("not at all ");

         } finally {

            System.out.print("amusing ");

         }

         System.out.print("yet compelling ");

      }

      System.out.print("man?");

   }

}

Dieser Code erzeugt je nach dem Wert von mysteriousState eine der folgenden Ausgaben:

1   Who is amusing

2   Who is that amusing man?

3   Who is that strange amusing Who is that strange amusing ...

4   Who is that strange but kindly amusing

5   Who is that strange but kindly not at all amusing yet compelling man?

In Fall 3 endet die Ausgabe erst, wenn Strg+C gedrückt wird. In Fall 4 wird auch eine von UncaughtException erzeugte Fehlermeldung ausgegeben.


Zusammenfassung

Heute haben Sie das Arbeiten mit Ausnahmen gelernt und erfahren, daß Sie damit Robustheit und Multithreading-Fähigkeit (lernen Sie morgen) in Ihrem Programmdesign erreichen können.

Sie haben eine Fülle von Ausnahmen kennengelernt, die in der Java-Klassenbibliothek definiert sind. Sie haben die Verwendung von throw, try und catch gelernt, um mögliche Fehler und Ausnahmen zu handhaben. Ferner haben Sie gesehen, daß die ausschließliche Arbeit mit der Java-Ausnahmenhandhabung dem Programmierer einige Einschränkungen auferlegt. Sie haben aber auch erfahren, daß diese Einschränkungen in anderer Hinsicht reich belohnt werden.

Schließlich haben Sie die finally-Klausel gelernt, mit der Sie sicher sein können, etwas zu erreichen, egal was.


Fragen und Antworten

F: Ich möchte das zuletzt von Ihnen aufgeführte Beispiel testen, weiß aber nicht wo getContext() herkommt?

A: Dieses Beispiel war nicht als ausführbares Programm gedacht. Sie können aber wie folgt eines daraus machen: Entfernen Sie die Klausel extends ContextClass in der ersten Zeile. Dann ersetzen Sie getContext() in der dritten Zeile durch Integer.parseInt(args[0]). Jetzt können Sie das Beispiel wie folgt kompilieren und ausführen:

java MyFinalExceptionClass N

wobei N den gewünschten mysteriösen Zustand bezeichnet.

F: Ich habe den Unterschied zwischen Exceptions, Errors und RuntimeExceptions noch nicht verstanden. Könnten Sie das nochmal auf andere Weise erklären?

A: Errors werden durch dynamische Verknüpfungs- oder virtuelle Maschinenprobleme verursacht und sind deshalb für die meisten Programme auf einer zu niedrigen Ebene, um sie behandeln zu können (obwohl ausgefeilte Entwicklungsbibliotheken und Umgebungen wahrscheinlich dazu in der Lage sind). RuntimeExceptions werden durch die normale Ausführung des Java-Codes erzeugt. Sie spiegeln zwar gelegentlich eine Bedingung wider, die explizit gehandhabt werden soll oder kann, sind aber meist auf einen Programmierfehler zurückzuführen und sollen lediglich eine Fehlermeldung ausgeben, damit der Fehler abgegrenzt werden kann. Exceptions, die nicht zu RuntimeExceptions gehören (z. B. IOExceptions), sind Bedingungen, die aufgrund ihrer Natur explizit durch einen robusten und gut durchdachten Code gehandhabt werden sollten. Die Java-Klassenbibliothek enthält einige davon, die extrem wichtig sind, um das System sicher und korrekt benutzen zu können. Der Compiler unterstützt Sie bei der Handhabung dieser Ausnahmen durch Kontrollen und Einschränkungen über die throws-Klausel.

F: Gibt es eine Möglichkeit, die straffen Einschränkungen, denen Methoden durch die throws -Klausel unterliegen, irgendwie zu umgehen?

A: Ja, die gibt es. Nehmen wir an, Sie haben lange genug nachgedacht und sind fest entschlossen, diese Einschränkung zu umgehen. Das ist fast nie der Fall, weil die richtige Lösung die ist, Ihre Methoden neu auszulegen, um die auszuwerfenden Ausnahmen entsprechend zu berücksichtigen. Wir stellen uns aber vor, daß Sie durch eine Systemklasse aus irgendeinem Grund in einer Zwangsjacke stecken. Ihre erste Lösung ist, von RuntimeException eine Subklasse für Ihre spezielle Ausnahme zu erstellen. Sie können damit nach Herzenslust Ausnahmen auswerfen, denn die throws-Klausel, die Sie belästigt hat, muß diese neuen Ausnahmen nicht enthalten. Müssen Sie viele solcher Ausnahmen unterbringen, besteht ein eleganter Ansatz darin, einige neue Ausnahmenschnittstellen in Ihre neuen Runtime-Klassen einzumischen. Welche dieser neuen Schnittstellen Sie mit catch auffangen wollen, steht Ihnen frei (keine der normalen Runtime-Ausnahmen muß aufgefangen werden), während eventuelle Überbleibsel der (neuen) Runtime-Ausnahmen (absolut zulässig) diese andernfalls lästige Standardmethode der Bibliothek durchlaufen können.

F: Mir sind die langen Ketten von catch-Klauseln noch nicht klar. Können Sie das vorherige Beispiel mit der Handhabung von Ausnahmen zeilenweise nochmal aufführen?

A: Aber gewiß doch:

try {

someReallyExceptionalMethod();

} catch (NullPointerException n) {

... // Handhabt NullPointerExceptions

} catch (RuntimeException r) {

... // Handhabt RuntimeExceptions, die keine NullPointerExceptions sind

} catch (IOException i) {

... // Handhabt IOExceptions

} catch (MyFirstException m) {

... // Handhabt MyFirstExceptions

} catch (Exception e) {

... // Handhabt Ausnahmen, die weder RuntimeExceptions,

noch IOExceptions noch MyFirstExceptions sind

} catch (Throwable t) {

... // Handhabt Throwables, die keine Ausnahmen sind (d. h. Fehler)

 }

F: Ich finde es zuweilen ganz schön lästig, Ausnahmebedingungen zu handhaben. Was kann mich eigentlich davon abhalten, mich durch eine Methode mit einer throws-Klausel abzuschotten? Beispielsweise so:

try { that AnnoyingMethod(); } catch (Throwable t) { }

Damit könnte ich doch alle Ausnahmen einfach ignorieren, oder?

A: Sie würden damit nichts außer Ihrem Gewissen abschotten. In manchen Fällen sollen Sie nichts unternehmen, weil das einfach für die Implementierung der betreffenden Methode das Richtige ist. In anderen Fällen müssen Sie sich aber wohl oder übel durch diese lästigen Prozeduren durchkämpfen. Sie gewinnen dadurch ja auch mehr Erfahrung. Auch der beste Programmierer sollte sich vor solchen öden Dingen nicht scheuen, zumal er reichlich dafür belohnt wird.


Copyright ©1996 Markt&Technik
Buch- und Software- Verlag GmbH
Alle Rechte vorbehalten. All rights reserved.

Schreiben Sie uns!

Previous Page TOC Index Next Page See Page