Kapitel 6: Ausnahmen

Werbung
Liste P: Programmieren mit Java
WS 2001/2002
Prof. Dr. V. Turau
FH Wiesbaden
Kapitel 6: Ausnahmen
Folie 103 : Ausnahmesituationen
Ein Programm ist robust, wenn es bei unvorhersehbaren Situationen ein deterministisches
Verhalten zeigt und nicht einfach sang und klanglos abstürzt.
Ein robustes Programm ist auf Ausnahmesituationen vorbereitet und kann nach deren
Auftreten sinnvoll weiterarbeiten.
Beispiele für Ausnahmesituationen sind:
IO-Fehler
Überlauf eines externen Speichermediums
Inkonsistente Datenstrukturen
Folie 104 : Behandlung von Ausnahmesituationen in C
In vielen Programmiersprachen muss die Behandlung von Ausnahmesituationen in das
Programm kodiert werden
Beispiel: Kopieren einer Datei in C:
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc, char * argv[]) {
int f1, f2, n;
char buf[BUFSIZE];
...
if ((f1 = open(argv[1], O_RDONLY, 0)) == -1)
error("Datei %s nicht gefunden", argv[1]);
if ((f2 = creat(argv[2])) == -1)
error("Datei %s kann nicht angelegt werden", argv[2]);
while ((n = read(f1, buf, BUFSIZE)) > 0)
if (write(f2, buf, n) != n)
error("Fehler beim Schreiben in Datei %s", argv[2]);
...
Folie 105 : Exception Handling
Konsequenzen:
Der Algorithmus ist nicht mehr gut zu erkennen
Fehlerprüfung und Algorithmus sind stark verschränkt
Ziel der Ausnahmebehandlung (Exception Handling)
Saubere Trennung zwischen Algorithmus und Fehlerbehandlung
Sprachkonstrukte zur Fehlerbehandlung zur Verfügung stellen
Fehlerbehandlung durch Anwender erzwingen
Fehlerbehandlung durch Compiler überprüfbar machen
Folie 106 : Exception Handling -- Das Prinzip
Eine Ausnahme wird erzeugt (geworfen, thrown), wenn eine unerwartete Situation auftritt
Die Erzeugung und das Werfen erfolgt aus dem Programmcode
Die Reaktion auf eine geworfene Ausnahme nennt man Behandlung (Abfangen, Handling)
Ausnahmen sind Instanzen von Ausnahmeklassen
Der Compiler überprüft, ob alle Ausnahmen, welche auftreten können, auch behandelt
werden
Folie 107 : Exception Handling -- Vereinbarung
Kann bei der Ausführung einer Methode oder eines Konstruktors eine Ausnahme geworfen
werden, so muss dies bei der Vereinbarung der Methode / des Konstruktors angezeigt werden
Zur Vereinbarung wird die throws-Klausel verwendet
Beispiele:
Klasse FileReader:
public FileReader(String name)
throws FileNotFoundException
public int read() throws IOException
Klasse URL:
public URL(String spec)
throws MalformedURLException
Folie 108 : Exception Handling
Kann eine Anweisung im Rumpf einer Methode oder eines Konstruktors eine Ausnahme
erzeugen, so hat der Programmierer zwei Möglichkeiten:
Die Methode oder der Konstruktor vereinbart die Ausnahme in ihrer throws-Klausel
Die Methode oder der Konstruktor kann die Ausnahme abfangen (mit einer
try-catch-Anweisung). In diesem Fall wird die Ausnahme nicht in die throws-Klausel
aufgenommen
Folie 109 : Exception Handling (Beispiel)
Eine Methode soll das erste Zeichen einer Textdatei auslesen.
Die folgende Implementierung kann nicht übersetzt werden:
public int readErstesZeichen(String dateiName) {
FileReader fr = new FileReader(dateiName);
return fr.read();
}
Fehlermeldungen:
unreported exception java.io.FileNotFoundException;
must be caught or declared to be thrown
unreported exception java.io.IOException; must be
caught or declared to be thrown
Folie 110 : Exception Handling (Beispiel)
Möglichkeit 1:
public int readErstesZeichen(String dateiName)
throws FileNotFoundException, IOException {
FileReader fr = new FileReader(dateiName);
return fr.read();
}
Die folgende Verwendung der Methode ist nicht möglich:
public static void main(String[] a) {
char c = readErstesZeichen(a[0]);
}
Die Methode main muss die Ausnahmen von readErstesZeichen behandeln oder
vereinbaren
Folie 111 : Exception Handling (Beispiel)
Möglichkeit 2:
public int readErstesZeichen(String dateiName) {
try {
FileReader fr = new FileReader(dateiName);
return fr.read();
} catch (FileNotFoundException fnfw) {
System.err.println(dateiName + " nicht gefunden");
return -1;
} catch (IOException ie) {
System.err.println("Lesefehler: " + dateiName);
return -1;
}
}
Die folgende Verwendung der Methode ist möglich:
public static void main(String[] a) {
char c = readErstesZeichen(a[0]);
}
Folie 112 : try-catch-finally-Anweisung
Ausnahmen werden durch try-catch-finally-Anweisungen behandelt:
try {
Anweisungen;
} catch (Ausnahme1 a1) {
Anweisungen;
} catch (Ausnahme2 a2) {
Anweisungen;
} finally {
Anweisungen;
}
Die finally-Anweisung ist optional
Es können nur Ausnahmen abgefangen werden, die auch geworfen werden können
Folie 113 : try-catch-finally-Anweisung
Wird innerhalb des try-Blocks eine Ausnahme geworfen, dann
werden die try-Blöcke von oben nach unten betrachtet
ist die erzeugte Ausnahme Instanz der angegebenen Ausnahmeklasse, so werden die
angegebenen Anweisungen ausgeführt
die restlichen try-Blöcke werden übersprungen
Ist eine finally-Anweisung vorhanden, so werden die Anweisungen ausgeführt
Die finally-Anweisung wird auf jeden Fall ausgeführt, auch wenn keine Ausnahme auftrat
Folie 114 : try-catch-finally-Anweisung
Eine return-Anweisungen in einer finally-Anweisung überschreibt andere
return-Anweisungen
Beispiel:
public int readZeichen() {
try {
return read();
} catch (Exception e) {
return 0;
} finally {
return -1;
}
}
Die Methode readZeichen gibt immer den Wert -1 zurück
Folie 115 : Beispiel 1
Lesen einer Textdatei und Ausgabe des Inhalts auf der Standardausgabe
try {
FileReader fr = new FileReader(name);
while ((zeichen = in.read()) != -1)
System.out.println((char) zeichen);
fr.close();
} catch (FileNotFoundException fnfw) {
System.err.println(name + " nicht gefunden");
} catch (IOException ie) {
System.err.println("Lesefehler: " + name);
}
Folie 116 : Beispiel 2
Falsche Reihenfolge der Ausnahmen (FileNotFoundException ist Unterklasse von
IOException)
try {
FileReader fr = new FileReader(name);
while ((zeichen = in.read()) != -1)
System.out.println((char) zeichen);
fr.close();
} catch (IOException ie) {
System.err.println("Lesefehler: " + name);
} catch (FileNotFoundException fnfw) {
System.err.println(name + " nicht gefunden");
//Dieser Code wird nie erreicht!
}
Folie 117 : Beispiel 3
Rückgabetypen beachten!
public int readErstesZeichen(String dateiName) {
try {
FileReader fr = new FileReader(dateiName);
return fr.read();
} catch (FileNotFoundException fnfw) {
System.err.println(dateiName + " nicht gefunden");
} catch (IOException ie) {
System.err.println("Lesefehler: " + dateiName);
return -1;
}
}
Fehler: Der erste catch-Block enthält keine return-Anweisung!
Folie 118 : Checked und Unchecked Exceptions
Es gibt zwei Gruppen von Ausnahmen: checked und unchecked Exceptions
Der Compiler überprüft nur die Behandlung von checked Exceptions
Unterklassen der Klasse RuntimeException sollen nie behandelt werden. Hierzu zählen unter
anderem:
NullPointerException
ArrayIndexOutOfBoundsException
ArithmeticException
Folie 119 : Klassenhierarchie
Unterklassen von RuntimeException sind Unchecked Exceptions (NullPointerException,
ArithmeticException, ArrayIndexOutOfBoundsException)
Unterklassen von Exception (außer Unterklassen von RuntimeException) sind Checked
Exceptions
Folie 120 : Beispiel
Negativbeispiel: Keine RuntimeExceptions abfangen
try {
File f = new File(a[0]);
} catch (ArrayIndexOutOfBoundsException e){
....
}
Negativbeispiel: Leere catch-Anweisung
public static void main(String[] a) {
try {
File f = new File(a[0]);
} catch (IOException e){}
Folie 121 : Debugging von Ausnahmen
Debugging von Ausnahmen: Die Methode printStackTrace
try {
File f = new File(a[0]);
.....
} catch (Exception e){
e.printStackTrace();
}
Ausgabe:
java.io.FileNotFoundException: test.txt
(Das System kann die angegebene Datei nicht finden)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileReader.<init>(Unknown Source)
at IOTest.readErstesZeichen(IOTest.java:152)
at IOTest.main(IOTest.java:7)
Folie 122 : Eigene Ausnahmeklassen
Benutzerdefinierte Ausnahmeklassen sollten checked exceptions sein
Dazu sollten sie Unterklasse von Exception sein
Beispiel: Bei der Methode pushString des Interfaces StringStack sollte verhindert werden,
dass null in den Stack eingeführt wird
public class StringStackException extends Exception {
public StringStackException() {
super("Illegales Stackelement: null");
}
}
Folie 123 : Verwendung
Die Methode pushString vereinbart jetzt die Ausnahme StringStackException
public void pushString(String s)
throws StringStackException;
Implementierung
public void pushString(String s)
throws StringStackException {
if (s == null)
throw new StringStackException();
...
}
Achtung unterschiedliche Schlüsselwörter: throws und throw
Folie 124 : Verwendung von Exceptions
Exceptions sollten nicht zur Programmsteuerung verwendet werden
try {
while(true) {
Object o = stack.pop();
...
} catch (EmptyStackException e) {}
Die Methode main sollte alle auftretenden Ausnahmen behandeln. D.h. folgende
Vereinbarung sollte in der Praxis nicht verwendet werden:
public static void main(String[] a) throws Exception {
Herunterladen