Eigene Ausnahmen
Ausnahmen selbst schreiben
- Man legt eine Subklasse von
Exception
an - Man gibt ihr einen Namen, der auf „Exception“ endet
- Man fügt Daten hinzu, die für die Ausnahme wichtig sind
- Man bietet passende Konstruktoren zu den Daten an, mindestens aber zwei Konstruktoren:
MyException()
MyException(String message)
Man kann nicht nur eigene Exceptions schreiben, man sollte es auch. Zu einem guten API gehören auch passende Ausnahmen. Allerdings sollte man dies nicht übertreiben und so häufig wie möglich auch vorhandene Exceptions der Java-Klassenbibliothek wieder verwenden, da man hier dem Verwender des APIs bereits bekannte Ausnahmen wie z. B. IllegalArgumentException
etc. anbieten kann.
Da bereits die Klasse Throwable
das Serializable
Interface implementiert (Details zu Serialisierung kommen später) muss man dafür sorgen, dass die selbstgeschriebenen Ausnahmen serialisierbar sind. Aus diesem Grund warnt Eclipse beim Anlegen einer neuen Exception, man solle eine serialVersionUID
vergeben. Diese Warnung können wir an dieser Stelle noch nicht diskutieren.
Ausnahmen selbst werfen
- Nur Subklassen von
Throwable
können geworfen werden - Im Allgemeinen wird man nur Runtime Exceptions und (Checked) Exceptions werfen
- Errors sind der VM vorbehalten
- Ausnahmen werden mit dem Schlüsselwort
throw
geworfen - Ausnahmen sollten frisch mit
new
erzeugt werden - Syntax:
throw new WhatEverException(...)
Da Ausnahmen ganz normale Java-Objekte sind, muss man nur eine entsprechende Klasse schreiben, um eine eigene Art von Ausnahmen zu definieren. Die Ausnahme-Objekte werden, wie alle Objekte, mit new
erzeugt.
Damit hat man aber erst ein Objekt erzeugt, es aber noch nicht auf den Weg gebracht. Hierzu muss man es mit dem Schlüsselwort throw werfen (Werfen von Ausnahmen).
Da Errors für die VM reserviert sind, sollten Sie ausschließlich Runtime- und Checked-Exceptions selbst schreiben und werfen. D. h., dass Sie nicht direkt von Throwable
ableiten, sondern immer von Exception
oder RuntimeException
.
Beispiel: Exceptions werfen
try {
System.out.println("A");
if (true) {
throw new IOException("Festplatte abgebrannt");
}
System.out.println("B");
}
catch (IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
A
Fehler: Festplatte abgebrannt
Das if (true)
ist an dieser Stelle tatsächlich nötig, weil der Compiler sonst die Zeile System.out.println("B")
als nicht erreichbaren Code (unreachable Code) gemeldet hätte.
Das Beispiel zeigt, dass man eine Ausnahme direkt nach dem Werfen mit throw
wieder einfangen kann.
public void method() {
try {
System.out.println("A");
werfer();
System.out.println("B");
}
catch (IOException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
private void werfer() throws IOException {
throw new IOException("Festplatte explodiert");
}
A
Fehler: Festplatte explodiert
Im Beispiel ist eine Methode werfer
zu sehen, die mit throw
eine Ausnahme vom Typ IOException
wirft und auch korrekt über throws
deklariert. Die Methode method
ruft werfer
auf und fängt die Exception sofort mit einem catch
. Die Ausgabe des Programms ist damit „A“ und dann die Nachricht aus der Ausnahme. Zur Ausgabe „B“ kommt es nicht mehr, weil vorher die Ausnahme auftritt und der try
-Block abrupt beendet wird.
Beispiel: Die Exception
public class ServerException extends Exception {
private int port;
private String host;
public ServerException(String message, String host, int port) {
super(message);
this.host = host;
this.port = port;
}
public int getPort() {
return port;
}
public String getHost() {
return host;
}
}
Man kann nicht nur vorhandene Ausnahmen (z. B. IOException
) werfen, sondern auch eigene Ausnahmen schreiben. Das Beispiel zeigt eine selbstgeschriebene Ausnahme mit dem Namen ServerException
. Diese enthält weitere Daten zum Problem, das die Ausnahme ausgelöst hat. Hier sind dies Informationen zu einer Netzwerkverbindung, die nicht aufgebaut werden konnte. port
und host
bezeichnen einen Server im Internet eindeutig.
Auf die übliche Vorgehensweise, bei einer Exception mindestens einen Default-Konstruktor und einen mit einer Nachricht anzubieten, wurde hier aus Platzgründen verzichtet. Folgende Konstruktoren sollten noch zur ServerException
hinzugefügt werden:
ServerException()
ServerException(String message)
ServerException(Throwable cause)
ServerException(String message, Throwable cause)
Beispiel: Der Werfer
public class Server {
public static void connect(String host, int port)
throws ServerException {
int result = connectInternal(host, port);
if (result == -1) {
throw new ServerException("Cannot connect to "
+ host + ":" + port, host, port);
}
// ...
}
private static native int connectInternal(String host, int port);
}
Die Klasse Server
symbolisiert eine Komponente, die Netzwerkverbindungen aufbauen kann. Die eigentliche Verbindung wird von der Methode connectInternal
hergestellt, die nativ, d. h. nicht in Java, sondern in C implementiert ist. Aus diesem Grund kann connectInternal
keine Ausnahmen werfen, sondern zeigt ein Verbindungsproblem durch einen Rückgabewert von -1
an.
Die Methode connect
ruft die interne Methode auf und wirft im Fehlerfall eine Ausnahme vom Typ ServerException
. Neben der menschen-lesbaren Informationen „Cannot connect to“ werden noch die entsprechenden Attribute host
und port
der Ausnahme gesetzt.
Beispiel: Der Fänger
public class Client {
public void doIt() {
try {
Server.connect("server1", 22);
} catch (ServerException e1) {
System.err.println("Keine Verbindung zum Server "
+ e1.getHost() + " auf Port " + e1.getPort());
try {
Server.connect("server2", 22);
} catch (ServerException e2) {
System.err.println("Ersatzserver geht auch nicht: "
+ e2.getHost() + " auf Port " + e2.getPort());
}
}
}
}
Die Klasse Client
versucht eine Verbindung mithilfe der Klasse Server
aufzubauen. Gelingt dies nicht, wird ein Ersatzserver versucht.
Man erkennt an diesem Beispiel zwei Dinge:
- try-catch-Blöcke können ineinander geschachtelt werden
- Informationen aus der Ausnahme können ausgewertet werden. Hier wird insbesondere nicht die englische Nachricht aus der Ausnahme verwendet, sondern
Client
übersetzt die Meldung unter Verwendung der Attribute der Ausnahme.