Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS 16 EXCEPTIONS 1 Einführung Während der Ausführung eines Programms können viele Arten von Fehlern auftreten. Ernste Fehler bzw. Errors erfordern eine sofortige Reaktion des Programms. Beispiele sind: kein Speicher mehr! Division durch Null (bei int) Zugriff auf ein nicht mehr vorhandenes Objekt Überschreiten von Arraygrenzen Fehler bzw. abnormale Situationen können auch nachträglich und "ohne Fehler" des Programms entstehen. Da solche Situationen hoffentlich nur selten auftreten, werden sie als Ausnahmen bzw. Exceptions bezeichnet. Beispiele sind: fehlende Datei auf Festplatte Falscheingabe des Benutzers Papierstau beim Drucker Verlust einer Netzwerkverbindung Man kennt also diverse Arten von Fehlern, die in der Regel verschieden sowie auf unterschiedlichen Stufen behandelt werden müssen. 16 Exceptions.doc, V7 © H. Diethelm Seite 1/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS 2 Schlechter Lösungsansatz Grundsätzlich könnten Ausnahmen mit Hilfe der uns bereits bekannten Kontrollstrukturen behandelt werden. Folgendes Programmgerüst zeigt einen möglichen Ansatz: public boolean doA() { .... } public boolean doB() { .... } public boolean doC() { .... } public void doABC() { boolean okA, okB, okC; okA = doA(); if (!okA) { handleProblemA(); } else { okB = doB(); if (!okB) { handleProblemB(); } else { okC = doC(); if (!okC) { handleProblemC(); } } } } Nachteile: Die Ausnahmen dominieren die Programmlogik. Das Programm ist schwierig lesbar. 16 Exceptions.doc, V7 © H. Diethelm Seite 2/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS Der Rückgabewert wird für die Ausnahmebehandlung "verschwendet". Eine mehrdeutige Interpretation des Rückgabewertes ist schlecht, z.B. "-1 für Fehler". 3 Lösungsansatz von Java Wie fast alle modernen Programmiersprachen unterstützt auch Java die Behandlung von Ausnahmen mit speziellen Sprachkonstrukten. Folgende Schlüsselwörter werden dafür verwendet: try, catch, finally, throws, throw Die Ausnahmebehandlung ist so realisiert, dass sich Normalfall (vgl. try) und Ausnahmefall (vgl. catch) im Sourcecode separieren lassen. Ein Objekt einer Exception-Klasse (oder Error-Klasse) signalisiert dabei eine aufgetretene Ausnahme. Das Objekt kann zudem die Ausnahmebehandlung detaillierter über die aufgetretene Ausnahme informieren. 3.1 Einführungsbeispiel 1 JDK-Dokumentation der Methode Integer.parseInt(...): 16 Exceptions.doc, V7 © H. Diethelm Seite 3/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS Sourcecode: import java.awt.*; import java.applet.Applet; import java.awt.event.*; public class ExceptionDemo1 extends Applet implements ActionListener { private TextField stringField, resultField; private Label resultLabel, stringLabel; public void init() { stringLabel = new Label("Integer-Value? "); resultLabel = new Label("Answer: "); stringField = new TextField(20); resultField = new TextField(20); resultField.setEditable(false); add(stringLabel); add(stringField); stringField.addActionListener(this); add(resultLabel); add(resultField); } public void actionPerformed(ActionEvent ev) { if (ev.getSource() == stringField) { try{ int number = Integer. parseInt(stringField.getText()); resultField.setText("Doubled Value is " +(2*number)); } catch (NumberFormatException ex) { resultField.setText("Error, retype!"); } } } } 16 Exceptions.doc, V7 © H. Diethelm Seite 4/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS Screen-Shots: Veranschaulichung: actionPerformed(...) try-Block mit ode Meth uf naufr parseInt(...) parseInt(...)-Aufruf Eine Ausnahmebedingung wirft NumberFormatException bzw. entsprechendes Objekt catch-Block mit NumberFormatExceptionHandler no M rm rü etho aler ck d sp en ru ng Erklärungen: Falls eine Ausnahme auftritt, geht die Kontrolle direkt zum catch-Block über. Die Klasse NumberFormatException bzw. entsprechende Exceptions sind unchecked. Mit anderen Worten: Der Compiler verlangt nicht zwingend, dass ein parseInt(...)-Aufruf, welcher bekanntlich eine NumberFormatException auslösen kann, von einem try-Block aus aufgerufen und diese Exception in einem catch-Block behandelt wird. Es ginge also auch wie folgt: 16 Exceptions.doc, V7 © H. Diethelm Seite 5/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS 3.2 Einführungsbeispiel 2 Sourcecode: import java.awt.*; import java.applet.Applet; import java.awt.event.*; public class ExceptionDemo2 extends Applet implements ActionListener { private TextField stringField, resultField; private Label resultLabel, stringLabel; public void init() { stringLabel = new Label("Integer-Value? "); resultLabel = new Label("Answer: "); stringField = new TextField(20); resultField = new TextField(20); resultField.setEditable(false); add(stringLabel); add(stringField); stringField.addActionListener(this); add(resultLabel); add(resultField); } public void actionPerformed(ActionEvent ev) { if (ev.getSource() == stringField) { int number = Integer. parseInt(stringField.getText()); resultField.setText("Doubled Value is " +(2*number)); } } } 16 Exceptions.doc, V7 © H. Diethelm Seite 6/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS Screen-Shots: Veranschaulichung: 16 Exceptions.doc, V7 © H. Diethelm Seite 7/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS Erklärungen: Falls in der Methode actionPerformed(...) eine NumberFormatException nicht mit einem catch-Block abgefangen wird, wird die Exception an jene Methode weitergeleitet, welche actionPerformed(...) aufgerufen hat usw. Die Exception wird so lange "nach oben weitergereicht", bis sie jemand behandelt. Im Extremfall ist dies das Laufzeitsystem (vgl. Screen-Shot, Stack der JVM). Im Gegensatz zur Klasse NumberFormatException ist z.B. die Klasse FileNotFoundException checked. In diesem Fall verlangt der Compiler zwingend, dass eine Methode, welche diese Exception auslösen kann, in einem try-Block aufgerufen und diese Exception im zugehörigen catch-Block an "Ort und Stelle" behandelt wird! Alternativ ist möglich, dass man die Exception explizit "nach oben weiterreicht". Dies geschieht mit Hilfe des Schlüsselwortes throws. Beide Varianten illustriert das nächste Beispiel: 16 Exceptions.doc, V7 © H. Diethelm Seite 8/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS 3.3 Einführungsbeispiel 3 JDK-Dokumentation des Konstruktors FileReader(...): Sourcecode: public void openFile1() { try { inFile = new BufferedReader(new FileReader("myfile.txt")); } catch (FileNotFoundException ex) { errorField.setText("missing File, enter again!"); } } public void openFile2() throws FileNotFoundException { inFile = new BufferedReader(new FileReader("myfile.txt")); } Erklärungen: Die erste Variante dürfte die Bessere sein, weil sie die Ausnahme unmittelbar behandelt und den Benutzer zur nochmaligen Eingabe auffordert. Die zweite Variante zeigt, wie man die Weitergabe der Exception in der Signatur der Methode mit Hilfe von throws angibt. 16 Exceptions.doc, V7 Seite 9/15 © H. Diethelm FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS 4 Exception-Klassen Alle Ausnahmen in Java sind als eigenständige Objekte der Klasse java.lang.Throwable oder einer Unterklasse von Throwable realisiert. Damit kann eine ganze Hierarchie von Ausnahmen aufgebaut werden. Folgendes Klassendiagramm zeigt nur einen kleinen Ausschnitt: Object equals():boolean Throwable getMessage():Str ing Error LinkageError Exception all unchecked VirtualMachine Error IOException ClassNotFound Exception all checked, except for RuntimeException RuntimeExcepti on printStackTrace() :void OutOfMemoryEr ror StackOverflowE rror FileNotFoundEx ception ArithmeticExce ption IndexOutOfBou ndsException IllegalArgument Exception ArrayIndexOutO fBoundsExcepti on NumberFormat Exception An der Wurzel dieser Hierarchie stehen die direkt von Throwable abgeleiteten Klassen Error und Exception. 16 Exceptions.doc, V7 © H. Diethelm Seite 10/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS Obwohl die genaue Trennung dieser beiden Klassen schwierig ist, sind Error-Ausnahmen typisch auf Systemfehler zurückzuführen, die vom Java-Programm nicht korrigiert werden können, z.B. "kein Speicher mehr". Solche Ausnahmen sind unchecked und ein Programm wird in der Regel nicht versuchen, ErrorAusnahmen abzufangen und zu behandeln. Die Ursachen von Ausnahmen der Klasse Exception und deren Unterklassen sind dagegen meistens auf Probleme des Anwendungsprogrammes zurückzuführen und müssen daher abgefangen werden, z.B. fehlende Datei auf der Festplatte. Diese Ausnahmen sind deshalb checked. Der Compiler verlangt also, dass Methodenaufrufe, die solche Ausnahmen auslösen können, in einem try-Block aufgerufen werden. Aus der Reihe tanzen einzig die Klasse RunTimeException und ihre Unterklassen, welche unchecked sind! Entsprechende Ausnahmen rühren typisch von Arithmetik- (vgl. Division durch Null) und Array-Operationen (vgl. unzulässiger Index) her. Weil solche Operationen sehr häufig vorkommen, ist es dem Programmierer überlassen, wie weit er diese Ausnahmen (z.B. ArithmeticException, ArrayIndexOutOfBoundsException) selber abfangen will; das Laufzeitsystem wird es spätestens tun. 16 Exceptions.doc, V7 © H. Diethelm Seite 11/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS 5 Benutzerdefinierte Exceptions Anwendung: Benutzerdefinierte Exception-Klasse: public class NoDigitException extends Exception { public NoDigitException(String s) { super(s); } } Benutzerdefinierte Methode, welche obige Exception wirft: public class NumberNames { private static String[] names = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; public static int convertToNumber(String name) throws NoDigitException { for (int i=0; i<10; i++) { if (names[i].equals(name)) return i; } throw new NoDigitException(name + " is not a digit!"); } } 16 Exceptions.doc, V7 © H. Diethelm Seite 12/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS Applet, das von obiger Methode Gebrauch macht: import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class TestNumberNames extends Applet implements ActionListener { private TextField inputField; outputField; public void init() { Label label = new Label("Name of a number (e.g. three)?"); add(label); inputField = new TextField (5); inputField.addActionListener(this); add(inputField); outputField = new TextField (20); outputField.setEditable(false); add(outputField); } public void paint (Graphics g) { } public void actionPerformed(ActionEvent ev) { try { outputField.setText("The number is " + NumberNames.convertToNumber (inputField.getText())); } catch (NoDigitException ex) { outputField.setText("Exception: " + ex.getMessage()); } } } 16 Exceptions.doc, V7 © H. Diethelm Seite 13/15 FHZ Hochschule für Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren 16 EXCEPTIONS 6 Exception-Hierachie und finally Beispiel: import java.net.*; ... try { // Versuch, eine Datei von einem WWW-Server // über eine URL zu laden: URL aURL = new URL("http://www.hta.fhz.ch/index.html"); ... } catch (MalformedURLException ex1) { // Schreibweise der URL (Protokoll/Adresse) // falsch ... } catch (UnknownHostException ex2) { // Server kann nicht ermittelt werden ... } catch (IOException ex3) { // Übertragung der Datei misslungen ... catch (Exception ex4) { // allgemeiner Fehler ... } finally { // in jedem Fall geöffnete Netzwerk// verbindung schliessen ... } ... 16 Exceptions.doc, V7 © H. Diethelm Seite 14/15 Abteilung Informatik, Fach Programmieren FHZ Hochschule für Technik+Architektur Luzern 16 EXCEPTIONS Bemerkungen: Es besteht die Möglichkeit, mit Hilfe von mehreren catchBlöcken sehr flexibel und differenziert auf verschiedene Ausnahmen zu reagieren. Die Anordnung der einzelnen catch-Blöcke ist relevant, weil die Blöcke von oben nach unten durchsucht werden, um eine Ausnahme abzufangen. Deshalb müssen die catch-Blöcke für Exception-Unterklassen immer vor ihren Oberklassen stehen, da sie sonst gar nicht erreichbar wären (vgl. speziellere vs. allgemeinere Ausnahmen)! Der Compiler reklamiert, falls diese Regel nicht eingehalten wird. Der finally-Block ist optional. Er wird in jedem Fall ausgeführt, entweder im Anschluss an den try- oder einen catchBlock. Er wird selbst dann ausgeführt, wenn im try- oder einem catch-Block mit einer return-Anweisung die Methode verlassen wird! Im finally-Block lassen sich typisch "Aufräumarbeiten" durchführen. Auf einen try-Block muss mindestens ein catch- oder finally-Block folgen. 16 Exceptions.doc, V7 © H. Diethelm Seite 15/15