16 Exceptions Zur Behandlung unerwarteter Situationen bietet Java Unterstützung in Form von Exceptions oder Ausnahmen. Den Sinn von Exceptions können wir Ihnen an einem kleinen Beispiel klarmachen. Nehmen Sie an, wir schreiben eine Methode, in welcher durch eine Zahl geteilt werden muß es könnte etwa aus der zurückgelegten Distanz und einem Zeitintervall berechnet werden, mit welcher Geschwindigkeit sich ein Auto bewegt. Eine erste Fassung einer entsprechenden Methode könnte wie folgt aussehen: Beispiel: Methode mit einem Fehler 1 static int geschwindigkeit( int distanz, int dauer ) 2 { 3 return distanz / dauer; 4 } Diese Methode wird problematisch, wenn von außen eine Dauer von 0 Sekunden angegeben wird, etwa im Aufruf int speed = geschwindigkeit( 10, 0 ); Was soll in solch einem Fall geschehen in der Methode würde nun offensichtlich versucht, durch 0 zu teilen. Dies wiederum würde einen Fehler erzeugen, welcher das Programm beenden würde. Nun ist es nicht besonders wünschenswert, daß ein Programm sich einfach beendet, wenn irgendwo ein ungültiger Zahlenwert erscheint. Das Programm sollte zumindest die noch nicht gespeicherten Daten speichern und Aufräumarbeiten durchführen (etwa die vom Programm erzeugen 5 Gigabyte temporärer Daten von der Festplatte löschen). Noch schöner wäre es natürlich, wenn das Programm einen Fehler melden würde. Man könnte nun vor jedem Aufruf der Funktion überprüfen, ob die Eingabedaten in Ordnung sind. Es ist aber durchaus denkbar, daß vor dem Aufruf einer Methode nicht von außen prüfbar ist, ob sie durchführbar ist stellen Sie sich eine Methode vor, welche auf eine Diskette zugreifen will. Hier hinge es davon ab, ob eine Diskette im Laufwerk liegt, ob die Methode funktioniert oder nicht. Beim Programmieren kommt es immer wieder zu Situationen, in denen unerwartete Daten oder unerwartete Ereignisse (Ausnahmen) vorliegen. Wenn eine Ausnahme auftritt, muß sie entweder dort, wo sie aufgetreten ist, behandelt werden, oder sie muß weitergegeben werden, so daß ein anderer Programmteil eine Möglichkeit hat, auf die Ausnahme zu reagieren. Wird die Ausnahme nirgends behandelt, bleibt als einzige Alternative der Abbruch des Programms mit einem Fehler. 16.1 Try catch Der try catch Block bietet eine Möglichkeit, Programmtext auszuführen, in welchem Ausnahmen auftreten könnten. Dabei ist eine Ausnahme ein Objekt einer gewissen Klasse, welches erzeugt wird, wenn eine unerwartete Situation aufgetreten ist. 145
Sie können sich das wie folgt vorstellen: An einer Stelle der Programmausführung tritt eine unerwartete Situation auf (z.b. Teilen durch Null, Diskette fehlt im Laufwerk). Als Reaktion auf diese Situation wird ein Ausnahme Objekt erzeugt, welches die aufgetretene Situation genauer beschreibt. Dieses Ausnahme Objekt wird nun weitergereicht ( geworfen ), bis irgendwo Programmtext gefunden wird, welcher das Ausnahmeobjekt auffängt und dann die Ausnahme behandelt. Wird kein Programmtext gefunden, der das Ausnahmeobjekt fängt, wird das Programm mit einer Fehlermeldung beendet. Eine Ausnahme kann nur gefangen werden, wenn sie innerhalb eines try catch Blocks auftritt. Die try catch Anweisung ermöglicht erst das Einfangen der Ausnahmeobjekte und schützt so einen gewissen Programmtextbereich vor einem ungewollten Programmabbruch. Man verwendet einen try catch Block, indem man den Programmtext, in welchem Ausnahmen Objekte geworfen werden könnten, in einen try Block setzt und dann für jeden Ausnahmetyp, der eingefangen werden soll, hinter den geschützten Block eine catch Anweisung setzt: try { geschuetzte Anweisungen;... } catch ( Ausnahmetyp1 x ) { Ausnahme-Anweisungen für Ausnahmetyp 1;... } catch ( Ausnahmetyp2 x ) { Ausnahme-Anweisungen für Ausnahmetyp 2;... } Der Block zwischen dem try und dem ersten catch ist hierdurch vor jeder Ausnahme geschützt, welche durch eine der catch Anweisungen aufgefangen werden kann. Sofern im geschützten Block eine Ausnahme auftritt(wie man selbst Ausnahmen erzeugt, erzählen wir gleich noch), wird die Ausführung des geschützten Blocks sofort abgebrochen, und die Ausführung wird in demjenigen Block ausgeführt, welcher auf das passende catch folgt. Was da genau passiert, wird nach dem folgenden Beispiel sicher klarer: Beispiel: Beispielsweise entsteht in Java beim ganzzahligen Teilen durch 0 eine Ausnahme des Typs ArithmeticException. Das folgende Programm demonstriert, wie man einen Programmteil vor einer solchen Ausnahme schützen kann: 146
Beispiel: Exceptions 1 a 1 class ExcDemo1 { 2 3 public static void main( String[] args ) { 4 int number = 0; 5 try { 6 System.out.println("Vor dem Teilen"); 7 System.out.println( 1 / number ); 8 System.out.println("Nach dem Teilen"); 9 } catch( ArithmeticException e ) { 10 System.out.println("Arithmetic Exception: " + e ); 11 } 12 } 13 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/excdemo1.java Der Programmtext von Zeile 6 bis Zeile 8 ist durch den umschliessenden try Block und das folgende catch Statement vor einem Programmabbruch durch alle Ausnahmen des Typs ArithmeticException geschützt. Beim Ablauf des Programmtextes entsteht in Zeile 7 beim Teilen durch 0 ein ArithmeticException Objekt, welches dann als Ausnahme geworfen wird. DadurchbrichtdieAusführungvonZeile 7sofort ab. Dain Zeile 9eine catch Anweisung für Ausnahmeobjekte des Typs ArithmeticException steht, wird das Programm dann in Zeile 10 fortgesetzt. Das gefangene ArithmeticException Ausnahmeobjekt kann in Zeile 10 unter der Variable e angesprochen werden. Durch die Aneinanderreihung mehrerer catch Blöcke kann man unterschiedliche Ausnahmen fangen und auf die unterschiedlichen Ausnahmen auch unterschiedlich reagieren. Beispiel: Spricht man in einem Array nicht existierende Indizes an, so wird eine ArrayOutOfBoundsException geworfen. Die Klasse ArrayOutOfBoundsException erbt von der Klasse IndexOutOfBoundsException. Im folgenden Programm wird sowohl das Teilen durch 0 als auch das Verwenden eines zu großen Index abgefangen: 147
Beispiel: Exceptions 2 a 1 class ExcDemo2 { 2 3 public static void main( String[] args ) { 4 int number[] = {1,0}; 5 for(int i=0; i < 3; i++) { 6 try { 7 System.out.println("Vor dem Teilen:" + i); 8 System.out.println( 1 / number[i] ); 9 System.out.println("Nach dem Teilen:" + i); 10 } catch( ArithmeticException e ) { 11 System.out.println("Arithmetic Exception: " + e ); 12 } catch( IndexOutOfBoundsException e ) { 13 System.out.println("Array out of bounds: " + e); 14 } 15 } 16 } 17 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/excdemo2.java 16.2 Ausnahmen werfen Ausnahmen entstehen entweder durch die mißbräuchliche Verwendung von Java Anweisungen oder dadurch, daß im Programmtext eine Ausnahme geworfen wird. Die bei der fehlerhaften Verwendung von Java Anweisungen entstehenden Ausnahmen sind im Java API im java.lang Paket beschrieben. Alle Ausnahmen müssen indirekt oder direkt von der Klasse Throwable erben. Von dieser Klasse vererben sich unter anderem die Klasse Exception, und von Exception ist eine Klasse RuntimeException abgeleitet. Alle Ausnahmen, die bei fehlerhafter Verwendung von Java Anweisungen auftreten kommen, sind von RuntimeException erbende Objekte. Wenn Sie selbst Ausnahmen definieren wollen, so sollten Sie diese in der Regel von der Klasse Exception oder einer ihrer Unterklassen ableiten (schauen Sie sich die genannten Klassen bitte in der JDK Dokumentation an). Sie können selbst Ausnahmen werfen, indem Sie ein Objekt der Klasse Throwable oder ein Objekt einer Unterklasse von Throwable erzeugen und dann mit der throw Anweisung die Ausnahme auslösen. Exception myexception = new Exception("Meine Ausnahme"); throw myexception; oder kürzer: throw new Exception(); Beispiel: So können Sie selbst eine Exception werfen und wieder fangen: 148
Beispiel: Throw 1 a 1 class ThrowDemo1 { 2 3 public static void main( String[] args ) { 4 int i = 0; 5 try { 6 System.out.println("Vor dem Werfen"); 7 if( i == 0 ) { 8 throw new Exception("Meine Ausnahme"); 9 }; 10 System.out.println("Nach dem Werfen: " + (1/i) ); 11 } catch( Exception e ) { 12 System.out.println("Exception: " + e ); 13 } 14 } 15 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/throwdemo1.java 16.3 Exceptions in Methoden Wenn bei der Ausführung einer Methode A eine Ausnahme geworfen wird, welche nicht in der Methode selbst gefangen wird, so wird die Methode beendet. Die Ausnahme wird dann an die Stelle übergeben, an der die Methode A aufgerufen wurde. Dort wird die Ausnahme erneut geworfen. Wurde die Methode A aus einer anderen Methode B aufgerufen, so gibt es zwei Möglichkeiten: Wenn der Aufruf der Methode A in Methode B von einem try catch Block geschützt wurde, welcher die erzeugte Ausnahme fangen kann, so wird das Programm im zugehörigen catch Block fortgesetzt. Wurde der Methode A jedoch ungeschützt aufgerufen, so wird Methode B abgebrochen, und die Ausnahme wird dort erneut geworfen, wo Methode B aufgerufen wurde. Dies setzt sich fort, bis ein try catch Block gefunden wird, welcher die geworfene Ausnahme fängt. Wird kein try catch Block gefunden, der die Ausnahme behandelt, so wird das Programm mit einer Fehlermeldung abgebrochen. Beispiel: Im folgenden Beispiel wird in der geschwindigkeit Routine eine Exception geworfen, wenn versucht wird durch 0 zu teilen. Die Ausnahme wird in der main Methode gefangen. 149
Beispiel: Exceptions in Methoden a 1 class MethodsDemo1 { 2 3 public static double geschwindigkeit( double distanz, double dauer ) 4 { 5 if( dauer == 0) { 6 throw new ArithmeticException("Teilen durch 0"); 7 } 8 return distanz / dauer; 9 } 10 11 public static void main( String[] args ) { 12 13 try { 14 for(int dauer = 5; dauer >= 0; dauer--) { 15 double distanz=5; 16 double v = geschwindigkeit(distanz, dauer); 17 System.out.print("" + distanz + " m in " + dauer 18 + " Sekunden sind eine"); 19 System.out.println("Geschwindigkeit von " + v + " m/s"); 20 } 21 } catch( Exception e ) { 22 System.out.println("Exception gefangen: " + e ); 23 } 24 } 25 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/methodsdemo1.java Wenn eine Methode eine Ausnahme zurückgibt, welche nicht zu den Java Laufzeitausnahmen gehört, muß dies im Kopf der Methode vermerkt werden. Die Java Laufzeitausnahmen (dies sind alle Ausnahmen, welche von der Klasse java.lang.runtimeexception vererbt sind) wird dies deshalb nicht gefordert, weil diese Ausnahmen fast jederzeit auftreten können und daher fast jede Methode eine RuntimeException werfen kann. Wäre aber im Kopf einer jeden Methoden vermerkt, daß sie eine Java Laufzeitausnahme werfen kann, so wäre das kein besonderer Informationsgewinn. In einer Methode wird im Methodenkopf beschrieben, welche Ausnahmen ihre Ausführung erzeugen kann, indem hinter der Kopfdeklaration der Text throws Ausnahmetyp1,..., AusnahmetypN folgt. Beispiel: Im folgenden Beispiel wirft die geschwindigkeit Methode eine eigene Ausnahme. Da diese Ausnahme keine Java Laufzeitausnahme ist, muß sie im Methodenkopf durch die throws Klausel deklariert werden. Wir deklarieren hier auch überflüssigerweise, daß die Routine eine ArithmeticException (dies ist eine Java Laufzeitausnahme) werfen kann. 150
Beispiel: Eigene Exceptions in Methoden a 1 class DistanzException extends Exception { 2 DistanzException(String s) { 3 super(s); 4 } 5 } 6 7 class MethodsDemo2 { 8 9 public static double geschwindigkeit( double distanz, double dauer ) 10 throws ArithmeticException, DistanzException 11 { 12 if( dauer == 0) { 13 throw new ArithmeticException("Teilen durch 0"); 14 } 15 if(distanz > 10) { 16 throw new DistanzException("Diese Distanz ist etwas groß"); 17 } 18 return distanz / dauer; 19 } 20 21 public static void main( String[] args ) { 22 23 try { 24 for(int dauer = 5; dauer >= 0; dauer--) { 25 double distanz=11; 26 double v = geschwindigkeit(distanz, dauer); 27 System.out.print("" + distanz + " m in " + dauer 28 + " Sekunden sind eine"); 29 System.out.println("Geschwindigkeit von " + v + " m/s"); 30 } 31 } catch( Exception e ) { 32 System.out.println("Exception gefangen: " + e ); 33 } 34 } 35 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/methodsdemo2.java Wenn in einer Methode A eine Methode B aufgerufen wird, welche eine Ausnahme von Typ X (abgesehen von RuntimeExceptions) erzeugen kann, so muß die von B erzeugte Ausnahme entweder innerhalb der Methode A gefangen werden, oder im Methodenkopf von A muß vermerkt sein, daß Methode A eine Ausnahme vom Typ X werfen kann. Andernfalls wird ein Kompilierfehler erzeugt. So wird sichergestellt, daß jede Methode, welche Ausnahmen werfen kann, dies im Methodenkopf vermerkt. Beispiel: Das folgende Beispiel kompiliert nicht korrekt. Der Compiler beschwert sich, daß die Methode test eine DistanzException werfen kann, ohne daß das im Kopf der Methode vermerkt ist. 151
Beispiel: Eigene Exceptions in Methoden a 1 class DistanzException extends Exception { 2 DistanzException(String s) { 3 super(s); 4 } 5 } 6 7 class MethodsDemo3 { 8 9 public static double geschwindigkeit( double distanz, double dauer ) 10 throws DistanzException { 11 if( dauer == 0) { 12 throw new ArithmeticException("Teilen durch 0"); 13 } 14 if(distanz > 10) { 15 throw new DistanzException("Diese Distanz ist etwas groß"); 16 } 17 return distanz / dauer; 18 } 19 20 public static void test() { 21 System.out.println( geschwindigkeit(11,3) ); 22 } 23 24 25 public static void main( String[] args ) { 26 try { 27 test(); 28 } catch( DistanzException e ) { 29 System.out.println("Habe Ausnahme gefangen: " + e); 30 } 31 } 32 } a Siehe http://www.wire.tu-bs.de/lehre/eipclasses/except/methodsdemo3.java Wir können das Beispiel reparieren, indem wir den Kopf der test Methode wie folgt ändern: public static void test() throws DistanzException Wir könnten das Beispiel auch reparieren, indem wir in der test Methode einen try catch Block einfügen. Die test Methode sähe dann so aus: public static void test() { try { System.out.println( geschwindigkeit(11,3) ); } catch( DistanzException e ) { System.out.println("Habe Ausnahme gefangen: " + e); } } 16.4 Ein paar abschließende Bemerkungen Immer wenn Sie Klassen aus dem Java API benutzen wollen, so müssen Sie in der Dokumentation des Java API nachsehen, ob die von Ihnen verwendeten Methoden eine Ausnahme werfen können. Wenn ja, so müssen Sie sich in Ihrem Programm um diese Ausnahme kümmern. 152
Wir haben nicht alle Aspekte von Ausnahmen besprochen. Wenn Sie sich dafür interessieren, lesen Sie es bitte im Buch Go To Java 2 (Kapitel 9) oder sonst irgendwo nach. 16.5 Zusammenfassung Nach Lesen dieses Kapitels sollten Sie erklären können, was eine Ausnahme ist. Beispielsituationen nennen können, in denen Ausnahmen auftreten. erklären können, wie Ausnahmen in Java repräsentiert werden. den try catch Befehl erklären können. beschreiben können, wie Ausnahmen behandelt werden, die innerhalb einer Methodenausführung auftreten. beschreiben können, was man beim Schreiben von Methoden berücksichtigen muß, in denen Ausnahmen entstehen können. erläutern können, was man beachten muß, wenn man JDK Funktionen verwendet, die Ausnahmen werfen können. 153