Programmieren I Fehlerbehandlung – Exceptions Heusch 2. Bd., 3 Ratz 10 Institut für Angewandte Informatik KIT – Die Forschungsuniversität in der Helmholtz-Gemeinschaft www.kit.edu Exceptions und ihre Behandlung Eine Exception ist eine Ausnahmesituation (wie z.B. Fehler) Eine Exception erzeugen (werfen, engl. „to throw“) bedeutet, eine Ausnahmesituation zu signalisieren. Eine Exception abfangen (engl. „to catch“) bedeutet, eine Ausnahmesituation zu behandeln, d.h. alle Aktionen durchzuführen, die notwendig sind, um auf die Situation angemessen zu reagieren. 2 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exception-Objekte In einem Programm wird eine Ausnahmesituation, die auftritt, durch ein Objekt repräsentiert, und zwar durch eine Instanz einer Unterklasse der Klasse java.lang.Throwable. Throwable besitzt 2 Standardunterklassen: java.lang.Error für Probleme z.B. beim dynamischen Laden oder in der JVM. Sie werden i.d.R. nicht abgefangen. java.lang.Exception für Ausnahmesituationen, die abgefangen und behoben werden können, z.B. Leseversuche über Dateiende hinaus (java.io.EOFException) oder Überschreitungen der Array-Grenzen (java.lang.ArrayIndexOutofBoundsException ) 3 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exception-Handling Exceptions können mit Hilfe von try-catch-finallyAnweisungen behandelt werden. Bestandteile: try-Block Der try-Block ist das Codestück, das überwacht werden soll (d.h. von dem Exceptions und abnormale Abbrüche behandelt werden sollen). catch-Blöcke Dem try-Block folgen null, ein oder mehrere catch-Blöcke, diejeweils bestimmte Exception-Typen abfangen und behandeln. finally-Block Den catch-Blöcken kann ein finally-Block folgen, der die trycatch-finally-Anweisung abschließt. Die Anweisungen im finally-Block werden immer ausgeführt, gleichgültig mit welchem Status der try-Block beendet wird. 4 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exception-Handling: So sieht der Code aus try { /* Programmcode, in dem eine Exceptions auftreten kann */ } catch (ExceptionType1 e1) { /* Code, der Exceptions vom Typ ExceptionType1 behandelt */ } catch (ExceptionType2 e2) { /* Code, der Exceptions vom Typ ExceptionType2 behandelt */ } finally { /* Code, der immer ausgeführt wird */ } e1, e2: Referenzvariablen auf die Exception-Objekte Man beachte: Innerhalb dieser Blöcke definierte Variablen sind außerhalb des jeweiligen Blocks nicht sichtbar! 5 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Beispiel für Exception-Handling: Konvertierung von String in ganze Zahl import java.util.Scanner; public class ExceptionExample { public static void main(String[] args) { try { System.out.print("Please enter an integer: "); Scanner scan = new Scanner(System.in); String input = scan.next(); // String einlesen int intNumber = Integer.parseInt(input); // Exception möglich System.out.println("Tripled: " + 3 * intNumber); } catch (NumberFormatException e) { System.err.println("Error during conversion: " + e.getMessage()); e.printStackTrace(); } } } 6 Beispieldialog: Please enter an integer: a Error during conversion: For input string: "a" ... W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Einige Methoden der Klasse Throwable String getMessage() Liefert den Beschreibungs-String der Exception. String toString() Liefert eine Beschreibung der Exception. void printStackTrace() Gibt den Call-Stack-Trace (Aufruf-Hierarchie) aus. Aufruf im Beispiel: e.printStackTrace(); Ausgabe im Beispiel (bei Eingabe von „a“): Ausgabe Error during conversion: For input string: "a" java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:492) at java.lang.Integer.parseInt(Integer.java:527) at slides.exceptions.ExceptionExample.main(ExceptionExample.java:17) 7 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Call-Stack-Trace: Beispiel aus der Praxis EJava.Exception.InvocationException:/uinbw/Entry/uinbw2GSA.html (Line: 36) failed to invoke method 'nextPageURL' on object Application.gsa.Search@132021a with parameters: [Ljava.lang.Object;@2803d5 at EJava.Reflection.JavaMethodInvocation.value(JavaMethodInvocation.java:141) at EJava.Scripting.AccessStatement.doProcess(AccessStatement.java:52) at EJava.Scripting.ScriptingElement.processWith(ScriptingElement.java:133) at EJava.Interpreter.processAll(Interpreter.java:286) at EJava.Interpreter.processFileHelper(Interpreter.java:266) at EJava.Scripting.EmbedStatement.doProcess(EmbedStatement.java:75) at EJava.Scripting.ScriptingElement.processWith(ScriptingElement.java:133) at EJava.Interpreter.processAll(Interpreter.java:286) at EJava.Scripting.ConditionalStatement.doProcess(ConditionalStatement.java:65) at EJava.Scripting.ScriptingElement.processWith(ScriptingElement.java:133) at EJava.Interpreter.processAll(Interpreter.java:286) at EJava.Interpreter.processFileHelper(Interpreter.java:266) at EJava.Scripting.EmbedStatement.doProcess(EmbedStatement.java:75) at EJava.Scripting.ScriptingElement.processWith(ScriptingElement.java:133) at EJava.Interpreter.processAll(Interpreter.java:286) … at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:468) at java.lang.Integer.parseInt(Integer.java:497) at Application.gsa.Search.nextPageURL(Search.java:779) at sun.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorIm.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at EJava.Reflection.JavaMethodInvocation.value(JavaMethodInvocation.java:131) … 8 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exceptions weiterleiten Java erfordert, dass jede Methode, in der eine Exception auftreten kann, diese Exception entweder abfängt und behandelt (try-catch-finally-Anweisung) oder an die aufrufende Methode "weiterwirft" (weiterleitet), indem im Methodenkopf in einer throws-Klausel der Typ der Exception angegeben wird. Zwei Beispiele für das Letztere: public void openFile(/*...*/) throws IOException { /* Hier stehen Anweisungen, die eine nicht abgefangene * java.io.IOException verursachen können. */ } public /* * * } 9 void myFunc(/*...*/) throws TooBigEx, TooSmallEx, DivByZeroEx { Hier stehen Anweisungen, die die nicht abgefangenen Exceptions TooBigEx, TooSmallEx und DivByZeroEx verursachen können. */ W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Eigene Exceptions erzeugen Bisher haben wir Standard-Exceptions von Java betrachtet. Diese sind Objekte der Klasse Throwable oder deren Unterklassen Error und Exception oder wiederum einer deren Unterklassen (siehe java.oracle.com). Zusätzlich zu diesen Klassen können auch eigene ExceptionKlassen deklariert werden und Exceptions ausgelöst werden. Beispiel für die Deklaration einer eigenen Exception-Klasse: class MyException extends Exception { public MyException(){ } public MyException(String message){ super(message); } } 10 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Eigene Exceptions: Weiteres Beispiel (1) // Deklaration der Exception-Klasse DivZeroException public class DivZeroException extends Exception { public DivZeroException(){ } public DivZeroException(String message){ super(message); } } 11 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Eigene Exceptions: Weiteres Beispiel (2) // Erzeugen und werfen der Exception public class MyMath { public static int divide(int a, int b) throws DivZeroException { if ( b == 0 ){ // Exception-Objekt erzeugen und werfen der Exception throw new DivZeroException("Division by zero!"); } else { return a / b; // Ganzzahldivision } } } 12 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Eigene Exceptions: Weiteres Beispiel (3) public class Test { public static void main(String[] args) { int res; // Abfangen und behandeln der Exception try { res = MyMath.divide(8, 0); System.out.println("Result: " + res); } catch (DivZeroException e) { System.out.println(e.getMessage()); } } } 13 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exception-Matching (1) Nach einem try-Block können mehrere catch-Blöcke auftreten. Der Exception-Handler sucht in den catch-Blöcken den ersten, der „passt“. Dabei ist kein „genaues passen“ erforderlich. Ein Exception-Objekt einer Unterklasse wird auch vom catch-Bock einer Oberklasse gefangen. Beispiel: class FatherException extends Exception { /*...*/ } class SonException extends FatherException { /*...*/ } public class FatherAndSon { public static void main(String[] args) { try { throw new SonException(); } catch( SonException s ){ System.err.println("Caught SonException"); } catch (FatherException f) { System.err.println("Caught FatherException"); } } Ausgabe: Caught SonException } 14 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Exception-Matching (2) Die SonException wird hier vom ersten catch-Block gefangen. Wenn der erste catch-Block gelöscht wird, so ist der Code immer noch lauffähig, da catch (FatherException f) alle FatherExceptions sowie von ihr abgeleiteten Exceptions fängt. Das Vertauschen der catch-Blöcke zu try { throw new SonException(); } catch (FatherException f) { System.err.println("Caught FatherException"); } catch( SonException s ){ System.err.println("Caught SonException"); } führt zu einem Fehler beim Compilieren, da der Compiler erkennt, dass der SonException-catch-Block nie erreicht werden kann. 15 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Runtime-Exceptions Bei RuntimeExceptions handelt es sich um bestimmte Laufzeitfehler, die an vielen Stellen im Programm auftreten können, z.B. Zugriff auf Arrays über die Arraygrenze hinaus (ArrayIndexOutOfBoundsException) Zugriff über eine leere Referenz (NullPointerException) NumberFormatException fehlerhafte Typkonvertierung (ClassCastException) RuntimeExceptions müssen nicht abgefangen werden („unchecked exceptions“). 16 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Erweiterungen seit Java 1.7 In Java sind seit 1.7 mehrere Exception-Typen in einem Catch-Block möglich (separiert durch "|") try { throwAorB(); } catch( ExceptionA | ExceptionB ex ){ throw ex; } Das erneute Werfen einer Exception in einem catch-Block (re-throw) erzeugt ein Objekt vom Typ der ursprünglich geworfenen Exception, und nicht mehr vom Typ der gefangenen Exception (die z.B. auch eine Oberklasse davon sein kann). Ab Java 1.7 17 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik Kleine Übungsaufgabe zu Exceptions Schreiben Sie im Package de.dhbwka.java.exercise.exceptions eine Exception-Klasse MyParameterException für eine ParameterAusnahmesituation (unerlaubter Parameterwert). Schreiben Sie im gleichen Package in einer Klasse MyMath eine eigene Methode static double mySqrt(double zahl) zur Berechnung der Quadratwurzel einer Zahl. Diese Methode soll für zahl< 0 eine MyParameterException werfen. Für zahl ≥ 0 soll der Rückgabewert gemäß dem Babylonischen-Wurzelziehen-Verfahren bestimmt werden – siehe Aufgabe „Babylonisches Wurzelziehen“ im Übungsblatt „Kontrollstrukturen“. (Zur Vereinfachung kann für zahl ≥ 0 der Rückgabewert auch mit Math.sqrt() bestimmt werden.) Schreiben Sie außerdem eine Test-Methode, die eine reelle Zahl einliest, mit dieser Zahl als Parameter die Methode mySqrt() aufruft, die MyParameterException von mySqrt() behandelt und den ExceptionString bzw. den Rückgabewert der Methode ausgibt. 18 W. Geiger, W. Süß, T. Schlachter, C. Schmitt Institut für Angewandte Informatik