Aus gegebenem Anlaß sollen die Programmiertechniken zur Fehlerbehandlung in Java einmal näher beleuchtet werde.
Prinzipiell gelten folgende Regeln:Für syntaktische Prüfungen von Kommandos ist die Shell zuständig. Welche Prüfungen genau erfolgen müssen, hängt von der Aufgabenstellung ab. Bei manchen Aufgaben kann z.B. von der syntaktischen Korrektheit der Eingaben ausgegangen werden. Das schließt Typ und Anzahl der Parameter ein. Andere Aufgaben fordern hier robustere Implementierungen.
Semantische Prüfungen werden in der entsprechenden Klasse implementiert, aber nicht automatisch immer ausgeführt, da dies zu unnötigem Performance-Verlusten führt. Ein Beispiel soll dies verdeutlichen:
class Graph {
void addEdge(int source, int target);
...
}
class Main {
public static void main(String argv[]) {
Graph graph = new Graph();
graph.addEdge(1, 2);
graph.addEdge(1, 2); // schon da, Fehler?
}
}
Eine automatische Prüfung, ob die Kante von 1->2 schon existiert, hat, falls der Graph mit Adjazenzlisten implementiert ist, eine Laufzeit von O(n) (worst case). Und das bei jeder Kante(!).
Häufig ist diese Prüfung auch nicht nötig, z.B. dann, wenn vom Programm eine disjunkte Menge von Kanten sequentiell eingefügt werden soll.
Ist eine Prüfung nötig, so sollte diese explizit durch den Programmierer erfolgen. Dazu muß die Klasse Graph nötige Prüffunktionen zur Verfügung stellen:
boolean hasEdge(int node1, int node2);
Diese Methode kann dann der Programmierer im Bedarfsfall von der Shell aus aufrufen, um Eingaben auf semantische Korrektheit zu prüfen.
Die Implementierung semantischer Prüfungen ist selten in der Shell sinnvoll, da dazu oft das Geheimnisprinzip verletzt werden müßte. Die Shell ruft dazu besser Prüfroutinen z.B. der Graph-Klasse auf, falls nötig.
Fehlermeldungen Fehlermeldungen können und sollen Sie in der Shell ausgeben. Die Definition dieser Meldungen als Konstanten ist möglich, aber nicht notwendig. Gerade Internationalisierung wird hier oft für Konstanten ins Feld geführt, die Funktioniert aber in Java über andere Mechanismen.
Konstanten können sinnvoll sein, wenn Sie Fehlermeldungen mehrfach verwenden.
Es ist vollkommen in Ordnung, wenn Sie Fehler etwa derartig behandeln:
...
if (!isValidEdge(source, target, distance)) {
System.err.println("Error! Edge not valid, check if source,"
+ " target and distance are in range");
} else if (...) {
...
}
...
Verzichten Sie auf eigene Fehlerbehandlungsklassen oder ähnliches.
Wenn, das gibt es so was in Java schon: Exceptions.
Benutzung von Exceptions. Generell gilt: Wenn Sie Exceptions werfen, dann nur selbstdefinierte. Keinesfalls sollten Sie RuntimeExceptions oder davon abgeleitete Klassen werfen. Leiten Sie auch selbst keine Klassen von RunTimeException oder Subklassen davon ab.
Wenn Sie eine Exception werfen, versehen Sie diese mit einer sinnvollen aussagekräftigen Fehlermeldung. Schreiben Sie z.B. statt:
"Fehler: keine Telefonnummer!"besser
"Fehler: Telefonnummer " + input + " hat ein ungültiges Format."
Exceptions sind immer dann sinnvoll, wenn eine einfache Prüfroutine das Problem nicht umgehen kann oder zu teuer wäre.
Als Beispiel soll die Graph Klasse eine Methode
List mst();haben, die einen minimalen Spannbaum berechnet. Das ist nur dann sinnvoll, wenn der gegebene Graph auch zusammenhängend ist. Ein expliziter Test auf Zusammenhang ist Zwar möglich, da der Algorithmus den Fehlerfall aber selbst erkennt und auch in O(#Kanten) läuft, ist eine separate Prüfung einfach zu teuer.
Fangen von Exceptions. Wenn Sie Exceptions fangen, dann beschränken Sie sich auf von Ihnen geworfene Exceptions.
Fangen Sie niemals Throwable oder Exception. Ausnahmen: keine!
Fangen Sie auch keine RuntimeExceptions oder davon abgeleitete Klassen. RuntimeExceptions entstehen meistens durch Programmierfehler oder eine falsche Benutzung von Klassen, korrigieren Sie diese Fehler anstatt sie zu reparieren.
IOExceptions sollen Sie nicht fangen. Geben Sie diese in main mit throws IOException weiter. Dies ist aber auch die einzige Exception-Klasse, die von main weitergegeben wird.
Beachten Sie, das Ihre Programme dennoch bei in der Aufgabenstellung als möglich definierten Eingaben nicht abstürzen dürfen, programmieren Sie sauber.
Wenn die Behandlung gewisser Fehler in der Aufgabenstellung gefordert wird, werden wir dies auch testen. Ein Absturz wird mit Funktionalität 'C' bewertet.