25.03.2004 YOU ARE HERE Ausnahmen (Exceptions) • Einführung • Objektorientierte Modellierung • Java-Sprachkonstrukte • im Programmablauf können Ausnahmesituationen auftreten • • • • • • • • • • • • z.B. Division durch Null, Datei nicht vorhanden, Übergabe eines ungültigen Parameters an eine Methode... • allgemein: Zustand, der das Programm daran hindert, im normalen Ablauf fortzufahren Pakete und Zugriffskontrolle Deklaration von Methoden, Attributen, Variablen Ausdrücke und Anweisungen Kontrollstrukturen Ausnahmebehandlung • Ziele des Entwicklers • Erstellen eines stabilen Programms • Trennung von fachlichem Code und Fehlerbehandlungscode Java-Klassenbibliothek Zeichenketten Dateien und Streams Datenstrukturen Networking Grafische Benutzungsoberflächen Extensible Markup Language Java-Praxiskurs • häufig einfacher, Ausnahmesituationen eintreten zu lassen und sie ordentlich zu behandeln, als sie unbedingt zu vermeiden 1 Java-Praxiskurs 2 Ausnahmebehandlung: Motivation Ausnahmebehandlung: Motivation • Wie werden Ausnahmesituationen in Methoden signalisiert? • Wie werden Ausnahmesituationen in Methoden signalisiert? • in vielen Programmiersprachen über bestimmten Rückgabewert, der als "Fehlersignal" vereinbart wird • besonders problematisch, wenn die Methode eigentlich kein Erfolgs/Fehlersignal liefern sollte, sondern einen fachlichen Rückgabewert public class Buch { private int jahr; // Erscheinungsjahr ... public boolean setJahr (int jahr) { if (jahr > 1452) { this.jahr = jahr; return true; // Signal "gültiger Parameter" } else { return false; // Signal "ungültiger Parameter" } } public class ... public int if (jahr ... // return } else { return } } Java-Praxiskurs Bibliothek { zahlDerNeuerscheinungen (int jahr) { > 1452) { zaehle in jahr erschienene Bücher gefundeneAnzahl; -1; // Signal "ungültiger Parameter" • mögliche Rückgabewerte der Methode • 0...n Zahl der gefundenen Bücher (bei gültigem Parameter) • -1 Fehlersignal (bei ungültigem Parameter) 3 Java-Praxiskurs Ausnahmebehandlung: Motivation Ausnahmebehandlung: Motivation • Wie werden Ausnahmesituationen erkannt? • Probleme bei dieser Art der Ausnahmebehandlung • Überprüfung des Rückgabewerts der Methode auf das vereinbarte Fehlersignal ... int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); switch (anzahlNeue) { case –1: System.out.println ("Ungültiges Suchjahr."); break; case 0: System.out.println ("Keine Buecher gefunden."); break; default: System.out.println (anzahlNeue + " Buecher gefunden."); System.out.println ("Buchliste anzeigen?"); ... } ... Java-Praxiskurs 5 Lehrstuhl für Angewandte Telematik/e-Business 4 • Fehler werden auf dem gleichen Weg zurückgegeben wie reguläre Rückgabewerte • Rückgabewert von Methoden ist aber meistens fachlich bestimmt und nicht gut zur Fehlersignalisierung geeignet ¾ Programmierer versuchen, Fehlersignale in Randbereichen des fachlichen Wertebereichs unterzubringen • Bedeutung so codierter Fehlersignale muss vereinbart werden • wenn überhaupt, passiert das nur informell in Kommentaren • anfällig für Missverständnisse bei der Entwicklung im Team ¾ Gefahr von falsch oder gar nicht behandelten Fehlercodes ¾ Seiteneffekte, Programmabbrüche, Apokalypse... Java-Praxiskurs 6 1 25.03.2004 Ausnahmebehandlung: Lösung Exceptions • eigener Kanal für Signalisierung von Fehlern • Fehlersignale in Java: Exceptions • von Exception abgeleitete Klassen ¾ Entkopplung der Signale vom regulären Rückgabewert • enthalten Methoden zur Beschreibung der Fehlersituation • eigene Datenstrukturen für Fehlersignale • von Java verwendete Exceptions (kleiner Auszug) ¾ differenzierte, genaue Fehlermeldungen • ArithmeticException, ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException, NegativeArraySizeException, FileNotFoundException, MalformedURLException, UnknownHostException u.v.m. • formelle Deklaration möglicher Fehlersignale ¾ keine Gefahr von Missverständnissen bei der Signalvereinbarung • erzwungene Behandlung von Fehlersignalen • selbstdefinierte Unterklassen von Exception ¾ keine vernachlässigten oder übersehenen Fehler • zur Signalisierung von Fehlersituationen in der eigenen Anwendung, z.B. UngueltigesJahrException, BuchBereitsVerliehenException, ... • Konvention: Exceptions werden mit Suffix Exception benannt ¾ robustere Programme Java-Praxiskurs 7 Java-Praxiskurs Deklarieren und Werfen von Exceptions Checked und unchecked Exceptions • Signalisieren eines Fehlers durch "Werfen" einer Exception • selbstdefinierte Exceptions müssen im throws-Teil des Methodenkopfs deklariert werden public class Bibliothek { ... public int zahlDerNeuerscheinungen (int jahr) throws UngueltigesJahrException { if (jahr > 1452) { ... // in jahr erschienene Bücher zaehlen return gefundeneAnzahl; // Wert zurückgeben } else { throw new UngueltigesJahrException(); // Fehlersignal } } ... } • Deklaration der Exception(s), die die Methode im Fehlerfall werfen kann Java-Praxiskurs ¾ reguläres Methodenverhalten aus der Signatur ablesbar ¾ Ausnahmeverhalten der Methode aus dem throws-Teil ablesbar • sowohl für den Programmierer als auch den Compiler • Compiler prüft, ob deklarierte Exceptions vom aufrufenden Programmteil abgefangen werden ("checked Exceptions") • einige von Java generierte Exceptions müssen nicht im throws-Teil deklariert werden • konkret: von RuntimeException abgeleitete Exceptions • signalisieren Laufzeitfehler, die bei umsichtiger Programmierung eigentlich nicht auftreten dürfen • könnten aber überall auftreten (z.B. NullPointerException) • Abfangen wird daher vom Compiler nicht überprüft ("unchecked") 9 Java-Praxiskurs Fangen von Exceptions: Deklaration Fangen von Exceptions: Ablauf • Eingrenzen des Programmteils, in dem Exceptions geworfen werden können: try-Block • wenn Exception auftritt, wird die Bearbeitung des tryBlocks abgebrochen und mit catch-Block fortgefahren ... try { int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0) System.out.println ("Keine Buecher gefunden."); else { System.out.println (anzahlNeue + " Buecher gefunden."); System.out.println ("Buchliste anzeigen?"); ... } } catch (UngueltigesJahrException e) { System.out.println ("Ungültiges Suchjahr."); } ... • Abfangen und Behandeln der Exception: catch-Block Java-Praxiskurs 8 10 ... try { int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0) System.out.println ("Keine Buecher gefunden."); else { System.out.println (anzahlNeue + " Buecher gefunden."); System.out.println ("Buchliste anzeigen?"); ... } } catch (UngueltigesJahrException e) { System.out.println ("Ungültiges Suchjahr."); } ... • wenn keine Exception, wird catch-Block übersprungen 11 Lehrstuhl für Angewandte Telematik/e-Business Java-Praxiskurs 12 2 25.03.2004 Behandlungsvarianten Fangen und andere Exception werfen • mit catch fangen und behandeln • z.B. um technische Fehlersignale in fachliche zu verwandeln • häufigste Form, wie gerade gezeigt public class Bibliothek { private Buch[] buchBestand; private int index = 0; public Buch naechstesBuch () throws ListenEndeException { try { index++; return buchBestand[index]; } catch (ArrayIndexOutOfBoundsException e) { throw new ListenEndeException(); } } } • mit catch fangen und eine andere Ausnahme werfen • z.B., um technische Fehlersignale in fachliche zu verwandeln und sie auf höherer Ebene behandeln zu lassen • nicht fangen, sondern "weiterfliegen" lassen • Fehlersignal nicht selbst behandeln, sondern von höherer Ebene behandeln lassen • Aufrufer von naechstesBuch muss sich zwar um ListenEndeException kümmern, muss aber nicht wissen, dass der Buchbestand als Array implementiert ist Java-Praxiskurs 13 Java-Praxiskurs Exception "weiterfliegen" lassen Exception-Behandlung durch JVM • ...um sie von höherer Ebene behandeln zu lassen • wenn Exception nirgends gefangen, sondern immer weiter nach oben "durchgereicht" wird, behandelt JVM sie public class Bibliothek { private Buch[] buchBestand; private int index = 0; Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at Bibliothek.naechstesBuch(Bibliothek.java:8) at Bibliothek.main(Bibliothek.java:13) public Buch naechstesBuch () throws ArrayIndexOutOfBoundsException { index++; return buchBestand[index]; } • Ausgabe der Exception mit Stack Trace } • Arrayzugriff kann ArrayIndexOutOfBoundsException erzeugen, die aber hier nicht gefangen wird • nicht gefangene Exceptions werden an aufrufende Methode weitergegeben ¾ Deklaration im throws-Teil des Methodenkopfes Java-Praxiskurs 15 • • • • Thread-Name Name der Exception ggf. Informationen zum Grund des Fehlers Call Stack (Aufrufhierarchie der Methode, die Exception erzeugte) mit Zeilennummern ¾ zielgerichtete Eingrenzung des Fehlers Java-Praxiskurs Werfen mehrerer Exceptions Fangen mehrerer Exceptions • komma-getrennte Deklaration möglicher Exceptions im Kopf • mehrere catch-Blöcke hintereinander public class Buch { ... public Buch (String autor, String titel, String verlag, int jahr) throws KeinTitelException, UngueltigesJahrException { this.autor = autor; if (titel != "") this.titel = titel; else throw new KeinTitelException(); this.verlag = verlag; if (jahr > 1452) this.jahr = jahr; else throw new UngueltigesJahrException(); } ... } Java-Praxiskurs 14 16 ... try { neuesBuch = new Buch (neuAutoren, neuTitel, neuVerlag, neuJahr); } catch (KeinTitelException e1) { System.out.println ("Kein Titel angegeben."); } catch (UngueltigesJahrException e2) { System.out.println ("Ungültiges Erscheinungsjahr."); } ... • wenn Exception auftritt, wird der passende catch-Block gesucht und ausgeführt 17 Lehrstuhl für Angewandte Telematik/e-Business Java-Praxiskurs 18 3 25.03.2004 Nachrichten in Exceptions speichern Nachrichten aus Exceptions lesen • Übergabe von Informationen über Fehlerursache beim Werfen im Konstruktor der Exception • beim Fangen wird die gerade erzeugte Referenz auf die Exception der Variable im catch-Ausdruck zugewiesen public class Buch { ... public Buch (String autor, String titel, String verlag, int jahr) throws UngueltigeEingabeException { this.autor = autor; if (titel != "") this.titel = titel; else throw new UngueltigeEingabeException("Kein Titel"); this.verlag = verlag; if (jahr > 1452) this.jahr = jahr; else throw new UngueltigeEingabeException("Jahr ungültig"); } ... } Java-Praxiskurs 19 • Zugriff auf die Methoden der Exception wie gewohnt per Punktnotation ... try { neuesBuch = new Buch (neuAutoren, "", neuVerlag, neuJahr); } catch (UngueltigeEingabeException e) { System.out.println (e.getMessage()); // Ausgabe Kein Titel } ... • Methoden (z.B. getMessage, toString...) werden von Oberklasse geerbt Java-Praxiskurs Eigene Exception-Klassen Klassenhierarchie bei Exceptions • Ableitung eigener Exceptions von Oberklasse Exception • hierarchische Deklaration von Exceptions üblich • Deklaration eines leeren Konstruktors und eines Konstruktors mit String-Parameter, um Exceptions beim Werfen erzeugen zu können public class EigeneException extends Exception { public EigeneException() { super(); } public EigeneException(String message) { super(message); } } • z.B. Exception Å RuntimeException Å IndexOutOfBoundsException Å ArrayIndexOutOfBoundsException • Vorsicht: catch-Blöcke für Ober-Exceptions fangen auch Unter-Exceptions! try {...} catch (Exception e1) { // faengt alles! System.out.println ("Ausnahmesituation"); } catch (ArrayIndexOutOfBoundsException e2) { // Fehler: nicht erreichbar! System.out.println ("Arraygrenze ueberschritten"); } • Erinnerungen • Bei der Ableitung werden Konstruktoren nicht mitvererbt, darum müssen wir sie selbst deklarieren • Wenn wir einen parametrisierten Konstruktor deklarieren, erzeugt Java keinen impliziten leeren Konstruktor, wir müssen ihn darum selbst deklarieren Java-Praxiskurs ¾ catch-Reihenfolge von speziellen zu generellen Exceptions 21 Java-Praxiskurs finally-Block Zusammenfassung • gelegentlich "Aufräumarbeiten" wie Schließen von Dateien nötig, unabhängig davon, ob Exception auftrat oder nicht • Deklaration in finally-Block nach letztem catch-Block • wird grundsätzlich nach try- bzw. catch-Block ausgeführt • Deklarieren und Werfen von Exceptions • auch, wenn im try/catch-Block return oder throw o.ä. steht try { ... // Anweisungen return; } catch (ArrayIndexOutOfBoundsException e) { ... throw new ListenEndeException(); } finally { ... // Aufräumarbeiten } Java-Praxiskurs 20 aufrufender Programmteil 22 typ methode (...) throws Exceptionklasse1, Exceptionklasse2 { ... throw new Exceptionklasse1(); // oder throw new Exceptionklasse2(Nachricht); ... } • Fangen und Behandeln von Exceptions ... try {...} catch (Exceptionklasse1 e) {...} catch (Exceptionklasse2 e) {...} finally {...} ... • Ableiten eigener Exceptions von Oberklasse Exception 23 Lehrstuhl für Angewandte Telematik/e-Business Java-Praxiskurs 24 4 25.03.2004 YOU ARE HERE Java-Klassenbibliothek • Einführung • Objektorientierte Modellierung • Java-Sprachkonstrukte Java-Klassenbibliothek • Zeichenketten • Dateien und Streams • Datenstrukturen • Networking • Grafische Benutzungsoberflächen • Extensible Markup Language • Sprachumfang von Java entspricht im Wesentlichen dem von anderen Sprachen Java-Praxiskurs • Mächtigkeit von Java bedingt durch umfangreiche Klassenbibliothek • Vorteil: im Wesentlichen plattformübergreifend verfügbar • Bibliothek in Pakete gegliedert • Pakete wie selbstdefinierte Pakete verwendbar 25 Überblick (Auszug) • • • • • • • • • • • java.applet java.awt java.beans java.io java.lang java.net java.rmi java.security java.sql java.util javax.swing Java-Praxiskurs 26 Java API Specification Applet-Programmierung GUI-Programmierung mit AWT JavaBeans™-Entwicklung Ein-/Ausgabe-Operationen, Datenströme fundamentale Klassen (z.B. String) Netzwerkfunktionen Remote Method Invocation Zertifikate, Kryptographie Datenbank-Funktionen Klassen für Datenstrukturen GUI-Programmierung mit Swing • und viele mehr (XML, CORBA, Namensdienste, Sound...) Java-Praxiskurs 27 Java-Praxiskurs YOU ARE HERE Klasse String • Einführung • Objektorientierte Modellierung • Java-Sprachkonstrukte • Java-Klassenbibliothek Zeichenketten • Dateien und Streams • Datenstrukturen • Networking • Grafische Benutzungsoberflächen • Extensible Markup Language • Zeichenketten sind String-Objekte mit Besonderheiten: Java-Praxiskurs 28 • Erzeugen von String-Objekten aus String-Literalen String a = "Hello", b = "World"; • Verknüpfen von Strings mit dem Operator + String c = a + " " + b; • Methoden funktionieren auch mit Literalen int laenge = "Literal".length(); // 7 • ansonsten verhalten Strings sich wie Referenztypen • == vergleicht Referenzen auf String-Objekte, nicht String-Inhalte • aber: String-Objekte sind unveränderbar ("immutable") • Zuweisung eines Literals oder Ausdrucks erzeugt neuen String • Methoden von String verändern den Objekt-Inhalt nicht, sondern geben ein neues String-Objekt zurück ¾ Vermeidung von Seiteneffekten 29 Lehrstuhl für Angewandte Telematik/e-Business Java-Praxiskurs 30 5 25.03.2004 String-Konstruktoren Methoden für String-Vergleiche • Leerstring • Referenzvergleich • String s = new String(); // "" • boolean result = (s == t); • true, wenn s und t die gleiche String-Referenz enthalten • String aus char-Array • inhaltlicher Vergleich • char[] c = {'J', 'a', 'v', 'a'}; String s = new String(c); // "Java" • boolean result = s.equals(t); • true bei Übereinstimmung aller Zeichen • boolean result = s.equalsIgnoreCase(t); • String aus anderem String (Kopie) • wie oben, ohne Berücksichtigung von Groß-/Kleinschreibung • String s = "Java"; String sKopie = new String(s); // "Java" • lexikographischer Vergleich ("wie im Telefonbuch") • int result = s.compareTo(t); int result = s.compareToIgnoreCase(t); • result < 0: s lexikographisch vor t • result == 0: s identisch mit t • result > 0: s lexikographisch hinter t • weitere Konstruktoren für spezielle Anwendungsfälle (Æ Java API Specification) Java-Praxiskurs 31 Java-Praxiskurs Methoden für Teilstring-Vergleiche Methoden zur String-Manipulation • Beginn mit bestimmter Zeichenkette? • ein Zeichen aus String herauskopieren • char charAt (int index) • boolean startsWith (String prefix) • Teilstring aus String herauskopieren • Ende mit bestimmter Zeichenkette? • String substring(int beginIndex, int endIndex) • boolean endsWith (String suffix) • Zeichen in String ersetzen • Position des ersten Auftretens eines bestimmten Zeichens • String replace(char oldChar, char newChar) • int indexOf (char ch) • Teilstring in String ersetzen (mit Regular Expressions) • Position des ersten Auftretens eines bestimmten Strings • String replaceAll(String regex, String replacemnt) • int indexOf (String s) • String in Groß- bzw. Kleinschreibung umwandeln • Position des letzten Auftretens eines bestimmten Zeichens • String toLowerCase() bzw. String toUpperCase() • int lastIndexOf (char ch) • Leerzeichen am Anfang und Ende entfernen • Position des letzten Auftretens eines bestimmten Zeichens • String trim() • Beachte: Methoden ändern nicht den Inhalt ihres StringObjekts, sondern geben neues String-Objekt zurück! • int lastIndexOf (String s) • oft existieren noch überladene Varianten dieser Methoden! Java-Praxiskurs 33 Java-Praxiskurs primitive Typen in Strings umwandeln Strings in primitive Typen umwandeln • valueOf-Methode (für viele Argumenttypen überladen) • in Wahrheitswert: • • • • • • • static static static static static static static String String String String String String String valueOf(boolean b) valueOf(char c) valueOf(char[] data) valueOf(double d) valueOf(float f) valueOf(int i) valueOf(long l) 34 • keine explizite Umwandlungsmethode; möglicher Ausdruck: • boolean bo = s.equals("true"); • in Zahlenwert: • parse...-Klassenmethoden von Zahlenklassen: • byte by = Byte.parseByte(s); • short sh = Short.parseShort(s); • int i = Integer.parseInt(s); • long l = Long.parseLong(s); • float f = Float.parseFloat(s); • double d = Double.parseDouble(s); • werfen NumberFormatException, wenn Umwandlung aufgrund von Formatfehlern unmöglich (z.B. "3.5" nicht in int wandelbar) • Klassenmethoden! ¾ Aufruf mit String.valueOf(...) String s; int i = 10; s = String.valueOf(i); // "10" Java-Praxiskurs 32 35 Lehrstuhl für Angewandte Telematik/e-Business Java-Praxiskurs 36 6 25.03.2004 String vs. StringBuffer Zusammenfassung • String-Objekte sind unveränderbar • Java-Klassenbibliothek • Manipulationen werden nicht auf dem ursprünglichen String ausgeführt, sondern auf einem dafür neu erzeugten String-Objekt • kann Performance beeinträchtigen, wenn sehr viele Änderungen eines Strings notwendig sind • mächtiger Funktionsumfang für viele Anwendungsbereiche • Dokumentation: Java API Specification von Sun • String • Abhilfe: StringBuffer-Objekte mit veränderbarem Inhalt • StringBuffer speichert eine Zeichenkette, die auch nachträglich geändert werden kann (ohne ein neues Objekt erzeugen zu müssen) • Vorteil: in bestimmten Anwendungen Performance-Gewinn möglich • Nachteil: Seiteneffekte wie bei anderen Referenztypen möglich • Erzeugen: StringBuffer sb = new StringBuffer("Hi"); • Anfügen: sb.append(" there"); // sb jetzt "Hi there" • in normalen String kopieren: String s = sb.toString(); • Empfehlung: Strings in der Regel komfortabler Java-Praxiskurs 37 Lehrstuhl für Angewandte Telematik/e-Business • • • • • • Referenztyp mit einigen Besonderheiten unveränderbar – Methoden erzeugen neue Objekte verschiedene Konstruktoren Methoden für String- und Teilstring-Vergleiche Methoden zur String-Manipulation Typumwandlung von und zu Strings • StringBuffer • enthält veränderliche Strings • für performancekritische Anwendungen mit vielen Manipulationen Java-Praxiskurs 38 7