Javakurs für Anfänger Einheit 13: Ausnahmen (Exceptions) Lorenz Schauer Lehrstuhl für Mobile und Verteilte Systeme Heutige Agenda Ausnahmen (Exceptions) Motivation Fehlerbehandlung Klassenhierarchie Checked vs. Unchecked Eigene Ausnahmen schreiben und werfen Praxis: Übung 1 Übung 2 Lernziele Einführung in die Fehlerbehandlung unter Java Mit Fehlern umgehen und diese behandeln können Eigene Ausnahmen definieren und verwenden können 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 2 Motivation Fehler sind unvermeidbar und gehören zum Programmieren dazu. Programmierfehler: Sollten im Programm behoben werden (Debugging) Benutzer-/Laufzeitfehler: Sollten abgefangen und behandelt werden Bsp.: Benutzer will die Wurzel einer negativen Zahl berechnen lassen Erfordert Ausnahmebehandlung, damit das Programm nicht abstürzt Kostet viel Zeit und Ressourcen (Tests) Ziel: Fehlerfreies Produkt! 1. Möglichkeit: Mögliche Fehler per if-Abfragen abfangen: // Falls kein Text eingegeben wurde: if(tf_user.getText().equals("")){ msg.setText("Es wurde kein Nutzername eingegeben!"); } 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 3 Exceptions als Reaktion auf Fehler Mit if-Abfragen könnten wir theoretisch arbeiten, aber Sehr umständlich und aufwendig Es müsste jede Möglichkeit für jedes Textfeld implementiert werden! Textfeld leer Eingabe hat falsches Format Eingabe ist ungültig etc. Daher nutzen wir Exceptions (Ausnahmen) für die Behandlung von Fehlern. Typisches Beispiel: Nutzer gibt eine Kommazahl, statt einer Ganzzahl ein: Bitte geben Sie eine Ganzzahl ein: 4.6 Exception in thread "main" java.lang.NumberFormatException: For input string: "4.6" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at Exceptions.FehlerhafteEingabe.main(FehlerhafteEingabe.java:12) 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 4 Behandlung von Fehlern Solche fehlerhaften Eingaben führen dazu, dass… der weitere Programmablauf nicht ausgeführt wird und stattdessen implizit eine Excepetion ausgelöst wird. Wenn wir solche Exceptions nicht behandeln (abfangen), dann geht es in unserem Programm hier nicht weiter => Das Programm bricht (teilweise) ab. Daher müssen wir auf mögliche Fehler/Ausnahmen reagieren: Mittels try catch können wir auftretende Exceptions abfangen: try{ <<Block>> }; Mit dem Schlüsselwort try wird der Versuch unternommen, den Programmcode im einschließenden Block, wie vorgesehen, auszuführen. catch (Exception e){ <<Block>>}; Die catch-Anweisung nach dem try-Block dient der Fehlerbehandlung, falls ein Fehler innerhalb des try-Blocks mit dem Typ Exception aufgetreten ist Falls kein Fehler im try-Block aufgetreten ist, wird der catch-Block übersprungen! 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 5 Beispiel mit try/catch public class FehlerhafteEingabe { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.println("Bitte geben Sie eine Ganzzahl ein: "); Hier kann Fehler vom Typ int eingabe=0; boolean show_ausgbabe=true; NumberFormatExcpetion auftreten! Daher mit try umschließen try { eingabe = Integer.parseInt(scan.next()); Falls NumberFormatExcpetion } catch (NumberFormatException e) { aufgetreten ist: System.out.println("Fehlerhafte Eingabe!"); catch-Block ausführen! show_ausgbabe=false; } if(show_ausgbabe) System.out.println("Ihre Eingabe war: "+eingabe); } } Bitte geben Sie eine Ganzzahl ein: 3 Ihre Eingabe war: 3 02.02.2017 Bitte geben Sie eine Ganzzahl ein: 4.5 Fehlerhafte Eingabe! Javakurs 13: Exceptions - Lorenz Schauer 6 Exceptions erben von Throwable Eine Ausnahme ist letztendlich auch wieder ein Objekt der Klasse Exception oder einer Unterklasse und kann „ganz normal“ mit new erzeugt werden Die Klasse Exception selbst erbt von der Klasse Throwable, die zahlreiche Methoden bereitstellt Neben Exceptions gibt es die Klasse Error: Ein Error bezeichnet „ernsthafte“ Problem wie bspw.: Speicherzugriffsfehler und sollte nicht durch die Anwendung behandelt werden! Quelle: http://openbook.rheinwerk-verlag.de/javainsel9/bilder/exceptionthrowableerroruml.gif 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 7 Klassenhierarchie von Exceptions Exceptions stammen also immer von Throwable ab und können über die Klassenhierarchie immer feiner spezifiziert sein: Ein kleiner Ausschnitt: Quelle: http://openbook.rheinwerk-verlag.de/javainsel9/bilder/exceptionsuml.gif 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 8 Mit catch auf bestimmte Fehler oder Fehlergruppen reagieren Durch die Klassenhierarchie können wir mittels catch(Fehlertyp f) auch auf einzelne spezielle Fehler reagieren Bsp.: NumberFormatException Unterklasse von java.lang.IllegalArgumentException Unterklasse von java.lang.RuntimeException Oder eben auf ganze Fehlergruppen: Bsp.: IllegalArgumentException Reagiert auch auf: IllegalChannelGroupException, IllegalCharsetNameException, IllegalFormatException, IllegalSelectorException, IllegalThreadStateException, InvalidKeyException, InvalidOpenTypeException, InvalidParameterException, InvalidPathException, KeyAlreadyExistsException, … Mittels catch(Exception e){…} reagieren wir auf jeglichen Fehler! Eher vermeiden! Lieber Fehler im Detail behandeln 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 9 Beispiel für detaillierte Fehlerbehandlung public class FehlerhafteEingabe { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.println("Bitte geben Sie eine Ganzzahl ein: "); int eingabe=0; boolean show_ausgbabe=true; try { eingabe = Integer.parseInt(scan.next()); } catch (NumberFormatException e) { System.out.println("Fehlerhafte Eingabe!"); show_ausgbabe=false; } catch (IllegalArgumentException e){ e.printStackTrace(); show_ausgbabe=false; } } } if(show_ausgbabe) System.out.println("Ihre Eingabe war: "+eingabe); 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer Auf einen try –Block können mehrere catch-Blöcke folgen Mit dem speziellsten Fehler beginnen! Dann in der Hierarchie immer höher gehen! Der erste catch-Block der passt wird ausgeführt! Die anderen werden dann übersprungen! 10 Das Schlüsselwort finally Try-catch Konstrukte können mit dem Schlüsselwort finally ergänzt werden Im finally-Block wird auf jeden Fall der Code ausgeführt, selbst wenn keine Ausnahme auftritt Motivation: Aufräumarbeiten o. Ä. try { } int eingabe = Integer.parseInt(scan.next()); System.out.println("Ihre Eingabe war: "+eingabe); } catch (NumberFormatException e) { System.out.println("Fehlerhafte Eingabe!"); } catch (IllegalArgumentException e){ e.printStackTrace(); } finally{ System.out.println("Programm ist zu Ende"); } Bitte geben Sie eine Ganzzahl ein: 3 Ihre Eingabe war: 3 Programm ist zu Ende 02.02.2017 Bitte geben Sie eine Ganzzahl ein: 3.4 Fehlerhafte Eingabe! Programm ist zu Ende Javakurs 13: Exceptions - Lorenz Schauer 11 Überprüft oder nicht? Ausnahmen können entweder nicht überprüft (unchecked) sein Können abgefangen werden, müssen aber nicht Idee: Ausnahmen, die auf einen Programmierfehler folgen, müssen nicht überprüft werden Alle Arten von java.lang.RuntimeException überprüft (checked) sein Müssen abgefangen oder explizit als möglich deklariert sein I.d.R. Ausnahmen, die ab und zu unvermeidlich sind Bsp.: FileNotFoundException, IOException,… //Fehlermeldung: unreported exception java.io.IOException; must \ be caught or declared to be thrown 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 12 Beispiel für checked Exceptions Beispiel: Thread.sleep(long millis) wirft eine InterruptedException Diese ist checked und muss behandelt werden! Dazu 2 Möglichkeiten: Der Methodenaufruf sleep() muss mit dem entspr. try-catch Block umgeben werden Die umgebene Methode muss explizit deklarieren, dass diese Ausnahme innerhalb ihres Rumpfs geworfen werden kann. Dann muss diese Ausnahme beim Methodenaufruf der umgebenen Methode abgefangen oder wieder deklariert werden 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 13 Beispiel für checked Exceptions public class FehlerhafteEingabe { 1. Möglichkeit: Der Aufruf der Methode sleep() wird mit einem try-catch Block umzingelt, welcher die InterruptedExcpetion behandelt public static void schlafen(int dauer){ } try { Thread.sleep(dauer); } catch (InterruptedException e) { e.printStackTrace(); } public static void main(String[] args) { FehlerhafteEingabe.schlafen(2000); } } //... public class FehlerhafteEingabe { public static void schlafen(int dauer) throws InterruptedException{ } Thread.sleep(dauer); public static void main(String[] args) { try { FehlerhafteEingabe.schlafen(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } } } //... 02.02.2017 2. Möglichkeit: Die Methode schlafen() deklariert explizit mit throws die InterruptedException Beim Methodenaufruf von schlafen() muss dann der Fehler abgefangen oder die umgebene Methode deklariert den Fehler mittels throws Statement Javakurs 13: Exceptions - Lorenz Schauer 14 Eigene Ausnahmen definieren Wie „ganz normale“ Klassen können wir auch eigene Ausnahmeklassen definieren Dabei erben wir von der entsprechenden Exception, um uns in die Klassenhierarchie an der gewünschten Stelle einzuordnen. Bsp.: Wir definieren eine eigene RuntimeException (unchecked) public class FalseSquareRootException extends RuntimeException{ public FalseSquareRootException(){ super("Kann Wurzel nicht berechnen"); } } Eigene Exception Klasse: Leerer Konstruktor belegt entsprechende Fehlermeldung Die eigene Exception wird explizit public static double berechneWurzel(double x){ geworfen mittels throw, wenn if(x<0){ Wurzel auf einer negativen Zahl throw new FalseSquareRootException(); berechnet werden soll. } Kann wieder abgefangen werden! return Math.sqrt(x); } Exception in thread "main" Exceptions.FalseSquareRootException: Kann Wurzel nicht berechnen at Exceptions.FehlerhafteEingabe.berechneWurzel(FehlerhafteEingabe.java:16) at Exceptions.FehlerhafteEingabe.main(FehlerhafteEingabe.java:25) 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 15 Weitere Infos Jede Ausnahme kennt aus der geerbten Klasse Throwable u.a. die Methoden: printStackTrace(); Gibt den Typ und den Text der Fehlermeldung aus und zudem die Folge der Methodenaufrufe bis zur ihrer Auflösung. Standardausgabe (auch von Eclipse) String getMessage(); Gibt den String der reinen Fehlermeldung zurück try { result = FehlerhafteEingabe.berechneWurzel(-1); System.out.println(result); } catch (FalseSquareRootException e2) { e2.printStackTrace(); } // oder: catch (FalseSquareRootException e2) { System.out.println(e2.getMessage()); } 02.02.2017 Ausgabe: Exceptions.FalseSquareRootException: Kann Wurzel nicht berechnen at Exceptions.FehlerhafteEingabe.berechneWu rzel(FehlerhafteEingabe.java:16) at Exceptions.FehlerhafteEingabe.main(Fehle rhafteEingabe.java:27) Ausgabe: Kann Wurzel nicht berechnen Javakurs 13: Exceptions - Lorenz Schauer 16 Programmieraufgabe zu Exceptions Erstellen Sie ein neues Eclipse-Projekt „Uebung13“ und schreiben Sie folgendes Programm: Legen Sie eine Array-Referenz vom Typ double[] an Fordern Sie nun den Benutzer auf, eine Ganzzahl einzugeben, welche die Größe des Arrays festlegen soll Erzeugen Sie nun das Array mit der Größe, die Sie vom Benutzer erhalten haben. Welche Fehler können dabei auftreten? Fange Sie jeden einzelnen Fehler gesondert in einem Catch-Block ab und reagieren Sie gesondert auf die falsche Nutzereingabe. Schreiben Sie auf jeden Fall „Vielen Dank für Ihre Eingabe!“ auf die Konsole am Ende des Programms. 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 17 Programmieraufgabe zu Exceptions Schreibe Sie ein weiteres Programm in Ihrem zuvor erstellten Projekt „Uebung13“: Schreiben Sie eine eigene Klasse Bruch, welche einen Dezimalbruch darstellen soll Die Klasse hat die beiden Instanzvariablen: Double zähler Double nenner Außerdem einen Konstruktor, welcher die beiden Instanzvariablen belegt Und einen leeren Konstruktor Daneben besitzt die Klasse die Methoden Setter- und Getter double berechneBruch(); Erzeugen Sie nun in Ihrer Main-Methode 2 Instanzen der Klasse Bruch Einmal mit dem leeren Konstruktor Und einmal mit dem Konstruktor, der Werte für Zähler und Nenner bekommt. Was passiert jeweils, wenn Sie die Methode berechneBruch() für Ihre beiden Instanzen aufrufen und sich das Ergebnis auf der Konsole anzeigen lassen wollen? 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 18 Programmieraufgabe zu Exceptions Fortführung der Aufgabe: Schreiben Sie einen eigenen Fehler „DivisionByZeroException“ und geben Sie diesem eine eindeutige Fehlermeldung mit. Bsp.: „Hast du in der Schule nicht gelernt, dass man nicht durch 0 teilen darf?“ Werfen Sie den Fehler, wenn die Methode berechneBruch() durch 0 teilen will. Fangen Sie den Fehler an einer geeigneten Stelle ab und geben Sie statt dem Ergebnis die Fehlermeldung aus! 02.02.2017 Javakurs 13: Exceptions - Lorenz Schauer 19