Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien zum Buch
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichen und Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
9 Ausnahmen müssen sein
10 Geschachtelte Typen
11 Besondere Typen der Java SE
12 Generics<T>
13 Lambda-Ausdrücke und funktionale Programmierung
14 Architektur, Design und angewandte Objektorientierung
15 Java Platform Module System
16 Die Klassenbibliothek
17 Einführung in die nebenläufige Programmierung
18 Einführung in Datenstrukturen und Algorithmen
19 Einführung in grafische Oberflächen
20 Einführung in Dateien und Datenströme
21 Einführung ins Datenbankmanagement mit JDBC
22 Bits und Bytes, Mathematisches und Geld
23 Testen mit JUnit
24 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 9 Ausnahmen müssen sein
Pfeil 9.1 Problembereiche einzäunen
Pfeil 9.1.1 Exceptions in Java mit try und catch
Pfeil 9.1.2 Geprüfte und ungeprüfte Ausnahmen
Pfeil 9.1.3 Eine NumberFormatException fliegt (ungeprüfte Ausnahme)
Pfeil 9.1.4 UUID in Textdatei anhängen (geprüfte Ausnahme)
Pfeil 9.1.5 Wiederholung abgebrochener Bereiche *
Pfeil 9.1.6 Bitte nicht schlucken – leere catch-Blöcke
Pfeil 9.1.7 Mehrere Ausnahmen auffangen
Pfeil 9.1.8 Zusammenfassen gleicher catch-Blöcke mit dem multi-catch
Pfeil 9.2 Ausnahmen weiterleiten, throws am Kopf von Methoden/Konstruktoren
Pfeil 9.2.1 throws bei Konstruktoren und Methoden
Pfeil 9.3 Die Klassenhierarchie der Ausnahmen
Pfeil 9.3.1 Eigenschaften des Exception-Objekts
Pfeil 9.3.2 Basistyp Throwable
Pfeil 9.3.3 Die Exception-Hierarchie
Pfeil 9.3.4 Oberausnahmen auffangen
Pfeil 9.3.5 Schon gefangen?
Pfeil 9.3.6 Ablauf einer Ausnahmesituation
Pfeil 9.3.7 Nicht zu allgemein fangen!
Pfeil 9.3.8 Bekannte RuntimeException-Klassen
Pfeil 9.3.9 Kann man abfangen, muss man aber nicht
Pfeil 9.4 Abschlussbehandlung mit finally
Pfeil 9.5 Auslösen eigener Exceptions
Pfeil 9.5.1 Mit throw Ausnahmen auslösen
Pfeil 9.5.2 Vorhandene Runtime-Ausnahmetypen kennen und nutzen
Pfeil 9.5.3 Parameter testen und gute Fehlermeldungen
Pfeil 9.5.4 Neue Exception-Klassen deklarieren
Pfeil 9.5.5 Eigene Ausnahmen als Unterklassen von Exception oder RuntimeException?
Pfeil 9.5.6 Ausnahmen abfangen und weiterleiten *
Pfeil 9.5.7 Aufruf-Stack von Ausnahmen verändern *
Pfeil 9.5.8 Präzises rethrow *
Pfeil 9.5.9 Geschachtelte Ausnahmen *
Pfeil 9.6 try mit Ressourcen (automatisches Ressourcen-Management)
Pfeil 9.6.1 try mit Ressourcen
Pfeil 9.6.2 Die Schnittstelle AutoCloseable
Pfeil 9.6.3 Ausnahmen vom close()
Pfeil 9.6.4 Typen, die AutoCloseable und Closeable sind
Pfeil 9.6.5 Mehrere Ressourcen nutzen
Pfeil 9.6.6 try mit Ressourcen auf null-Ressourcen
Pfeil 9.6.7 Unterdrückte Ausnahmen *
Pfeil 9.7 Besonderheiten bei der Ausnahmebehandlung *
Pfeil 9.7.1 Rückgabewerte bei ausgelösten Ausnahmen
Pfeil 9.7.2 Ausnahmen und Rückgaben verschwinden – das Duo return und finally
Pfeil 9.7.3 throws bei überschriebenen Methoden
Pfeil 9.7.4 Nicht erreichbare catch-Klauseln
Pfeil 9.8 Harte Fehler – Error *
Pfeil 9.9 Assertions *
Pfeil 9.9.1 Assertions in eigenen Programmen nutzen
Pfeil 9.9.2 Assertions aktivieren und Laufzeit-Errors
Pfeil 9.9.3 Assertions feiner aktivieren oder deaktivieren
Pfeil 9.10 Zum Weiterlesen
 

Zum Seitenanfang

9.3    Die Klassenhierarchie der Ausnahmen Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Ausnahme ist ein Objekt, dessen Typ direkt oder indirekt von java.lang.Throwable abgeleitet ist. (Die Namensgebung mit -able legt eine Schnittstelle nahe, aber Throwable ist eine nichtabstrakte Klasse.) Wir haben selbst wenig mit Throwable zu tun, sondern eher mit der direkten Unterklasse Exception bzw. den davon abgeleiteten Ausnahmeklassen.

 

Zum Seitenanfang

9.3.1    Eigenschaften des Exception-Objekts Zur vorigen ÜberschriftZur nächsten Überschrift

Das Ausnahmeobjekt, das uns in der catch-Klausel übergeben wird, ist reich an Informationen. So lässt sich erfragen, um welche Ausnahme es sich eigentlich handelt und wie die Fehlernachricht heißt. Auch der Stack-Trace lässt sich erfragen und ausgeben:

Listing 9.8     src/main/java/com/tutego/insel/exception/NumberFormatExceptionElements.java, main()

try {

Integer.parseInt( "19%" );

}

catch ( NumberFormatException e ) {

String name = e.getClass().getName();

String msg = e.getMessage();

String s = e.toString();



System.out.println( name );// java.lang.NumberFormatException

System.out.println( msg ); // For input string: "19%"

System.out.println( s ); // java.lang.NumberFormatException: For input string: "19%"



e.printStackTrace();

}

Im letzten Fall, mit e.printStackTrace(), bekommen wir auf dem Fehlerkanal System.err das Gleiche ausgegeben, was uns die virtuelle Maschine ausgibt, wenn wir die Ausnahme nicht abfangen:

java.lang.NumberFormatException

For input string: "19%"

java.lang.NumberFormatException: For input string: "19%"

java.lang.NumberFormatException: For input string: "19%"

at java.base/java.lang.NumberFormatException.forInputString(

NumberFormatException.java:65)

at java.base/java.lang.Integer.parseInt(Integer.java:652)

at java.base/java.lang.Integer.parseInt(Integer.java:770)

at c.t.i.e.NumberFormatExceptionElements.main(NumberFormatExceptionElements.java:7)

Die Ausgabe besteht aus dem Klassennamen der Exception, der Meldung und dem Stack-Trace. printStackTrace(…) ist parametrisiert und kann auch in einen Ausgabekanal geschickt werden.

 

Zum Seitenanfang

9.3.2    Basistyp Throwable Zur vorigen ÜberschriftZur nächsten Überschrift

Alle Ausnahmen sind Unterklassen von Throwable. Von dort aus verzweigt sich die Hierarchie der Ausnahmearten nach java.lang.Exception und java.lang.Error. Die Klassen, die aus Error hervorgehen, sollen hier nicht weiterverfolgt werden. Es handelt sich bei ihnen um so schwerwiegende Ausnahmen, dass sie zur Beendigung des Programms führen und vom Programmierer nicht weiter beachtet werden müssen und sollten. Throwable vererbt eine Reihe von nützlichen Methoden, die in Abbildung 9.4 zu sehen sind. Sie fasst gleichzeitig die Vererbungsbeziehungen noch einmal zusammen.

UML-Diagramm der wichtigen Oberklasse »Throwable«

Abbildung 9.4     UML-Diagramm der wichtigen Oberklasse »Throwable«

 

Zum Seitenanfang

9.3.3    Die Exception-Hierarchie Zur vorigen ÜberschriftZur nächsten Überschrift

In der Bibliothek sind alle Ausnahmeklassen von java.lang.Exception abgeleitet. Die Exceptions sind Fehler oder Ausnahmesituationen, die uns von behandelt werden sollten. Die Klasse Exception teilt sich dann nochmals in weitere Unterklassen bzw. Unterhierarchien auf. Abbildung 9.5 zeigt einige Unterklassen der Klasse Exception.

Ausgewählte Unterklassen von »Exception«

Abbildung 9.5     Ausgewählte Unterklassen von »Exception«

 

Zum Seitenanfang

9.3.4    Oberausnahmen auffangen Zur vorigen ÜberschriftZur nächsten Überschrift

Eine Konsequenz der Hierarchien besteht darin, dass es ausreicht, eine Ausnahme der Oberklasse aufzufangen. Wenn zum Beispiel eine FileNotFoundException auftritt, ist diese Klasse von IOException abgeleitet, was bedeutet, dass FileNotFoundException eine Spezialisierung darstellt. Wenn wir jede IOException auffangen, behandeln wir damit auch gleichzeitig die FileNotFoundException mit (siehe Abbildung 9.6). Nehmen wir folgende Behandlung an:

try {

...

}

catch ( FileNotFoundException e ) {

e.printStackTrace();

}

catch ( IOException e ) {

e.printStackTrace();

}

Da die Behandlung in den catch-Blöcken identisch ist, kann catch (IOException e) die FileNotFoundException gleich mitfangen:

try {

...

}

catch ( IOException e ) {

e.printStackTrace();

}

Auch wenn die Ausnahme über eine Oberklasse aufgefangen wird, lässt sich die Ausnahme prinzipiell später wieder mit instanceof identifizieren. Wir könnten schreiben:

catch ( IOException e ) {

if ( e instanceof FileNotFoundException )

System.err.println( "Datei ist nicht vorhanden!" );

else

System.err.println( "Allgemeiner Ein-/Ausgabefehler!" );

}

Aus Gründen der Übersichtlichkeit sollte diese Technik jedoch sparsam angewendet werden.

»IOException« im Klassendiagramm

Abbildung 9.6     »IOException« im Klassendiagramm

 

Zum Seitenanfang

9.3.5    Schon gefangen? Zur vorigen ÜberschriftZur nächsten Überschrift

Der Java-Compiler prüft, ob Ausnahmen vielleicht schon in der Kette aufgefangen wurden, und meldet einen Fehler, wenn catch-Blöcke nicht erreichbar sind. Wir haben gesehen, dass FileNotFoundException eine spezielle IOException ist und ein catch(IOException e) Ausnahmen vom Typ FileNotFoundException gleich mit fängt.

try {

...

}

catch ( IOException e ) { // fange IOException und alle Unterklassen auf

}

Natürlich kann eine FileNotFoundException weiterhin als eigener Typ aufgefangen werden, allerdings ist es wichtig, die Reihenfolge der catch-Blöcke zu beachten. Denn die Reihenfolge ist absolut relevant; die Typtests beginnen oben und laufen dann weiter nach unten durch. Wenn ein früher catch schon Ausnahmen eines gewissen Typs abfängt, also etwa ein catch auf IOException alle Ein-/Ausgabefehler, so ist ein nachfolgender catch auf die FileNotFoundException falsch.

Nehmen wir an, ein try-Block kann eine FileNotFoundException und eine IOException auslösen. Dann ist die linke Behandlung aus Tabelle 9.1 korrekt, aber die rechte falsch:

richtig

mit Compilerfehler

try {

...

}

catch ( FileNotFoundException e ) {

}

catch ( IOException e ) {

}
try {

...

}

catch ( IOException e ) {

}

catch ( FileNotFoundException e ) { // inline image

}

Tabelle 9.1     Die Reihenfolge der »catch«-Blöcke spielt eine Rolle.

[»]  Hinweis

Der folgende multi-catch ist falsch:

try {

new RandomAccessFile( "", "" );

}

catch ( FileNotFoundException | IOException | Exception e ) { }

Der Java-Compiler meldet einen Fehler der Art »Alternatives in a multi-catch statement cannot be related by subclassing« und bricht ab.

Mengenprüfungen führt der Compiler auch ohne multi-catch durch, und Folgendes ist, wie gerade besprochen, ebenfalls falsch:

try { new RandomAccessFile( "", "" ); }

catch ( Exception e ) { }

catch ( IOException e ) { }

catch ( FileNotFoundException e ) { }

Während eine Umsortierung der Zeilen die Fehler korrigiert, spielt die Reihenfolge bei multi-catch keine Rolle.

 

Zum Seitenanfang

9.3.6    Ablauf einer Ausnahmesituation Zur vorigen ÜberschriftZur nächsten Überschrift

Das Laufzeitsystem erzeugt ein Ausnahmeobjekt, wenn ein Fehler über eine Exception angezeigt werden soll. Dann wird die Abarbeitung der Programmzeilen sofort unterbrochen, und das Laufzeitsystem steuert selbstständig die erste catch-Klausel an (oder springt weiter zum Aufrufer bei einem throws oder einer ungeprüften Ausnahme). Wenn die erste catch-Klausel nicht zur Art der aufgetretenen Ausnahme passt, werden der Reihe nach alle übrigen catch-Klauseln untersucht, und die erste übereinstimmende Klausel wird angesprungen (oder ausgewählt). Erst wird etwas versucht (daher heißt es im Englischen try), und wenn im Fehlerfall ein Exception-Objekt im Programmstück ausgelöst und geworfen (engl. throw) wird, lässt es sich an einer Stelle auffangen (engl. catch). Da immer die erste passende catch-Klausel ausgewählt wird, darf im Beispiel die letzte catch-Klausel keinesfalls zuerst stehen, da diese auf jede IOException passt, und FileNotFoundException ist eine Unterklasse von IOException. Alle anderen Anweisungen in den catch-Blöcken würden dann nicht ausgeführt; der Compiler erkennt dieses Problem und gibt einen Fehler aus.

 

Zum Seitenanfang

9.3.7    Nicht zu allgemein fangen! Zur vorigen ÜberschriftZur nächsten Überschrift

Löst ein Programmblock etwa eine IOException, MalformedURLException und eine FileNotFoundException aus und sollen die drei Ausnahmen gleich behandelt werden, so fängt ein catch (IOException e) die beiden Typen FileNotFoundException und MalformedURLException gleich mit ab, da beide Unterklassen von IOException sind. So behandelt ein Block alle drei Ausnahmetypen. Das ist praktisch.

Nun gibt es jedoch auch Ausnahmen, die in der Vererbungsbeziehung nebeneinanderliegen, wie InputMismatchException und IOException. Erinnern wir uns noch einmal an das Dateibeispiel der Klasse UserInputUuidWriterMultiCatch in Abbildung 9.6, wo wir ein multi-catch eingesetzt haben:

try {

...

}

catch ( InputMismatchException | IOException e ) {



}

Da InputMismatchException und IOException Unterklassen von Exception sind, lässt sich im Prinzip auch schreiben:

try {

...

}

catch ( Exception e ) {

...

}

Die hier gewählte Lösung, in der Ausnahmehierarchie so weit nach oben zu laufen, bis eine gemeinsame Oberklasse gefunden wurde, ist nicht gut. Denn was für Ausnahmetypen gut funktioniert, die sowie in der Hierarchie liegen (wie MalformedURLException, was eine IOException ist), ist catch(Exception e) gefährlich, weil damit viel mehr aufgefangen und in der Ausnahmebehandlung behandelt wird. Taucht beispielsweise eine null-Referenz durch eine nicht initialisierte Variable mit Referenztyp auf und kommt es zu einer NullPointerException, so würde dies fälschlicherweise ebenso mitgefangen – der Programmfehler hat aber nichts mit der InputMismatchException oder IOException zu tun:

try {

Point p = null;

p.x = 2; // inline image NullPointerException

int i = 0;

int x = 12 / i; // inline image Ganzzahlige Division durch 0



irgendwas kann InputMismatchException auslösen ...

irgendwas kann IOException auslösen ...

}

catch ( Exception e ) { Behandlung }

Eine NullPointerException und die ArithmeticException sollen nicht mitbehandelt werden. Das zentrale Problem ist hier, dass diese Ausnahmen ungeprüfte Ausnahmen vom Typ RuntimeException sind und RuntimeException eine Unterklasse von Exception ist. Fangen wir alle Exception-Typen, so wird alles mitgefangen – und RuntimeException eben auch. Es ist nicht möglich, alle Nicht-Laufzeitausnahmen abzufangen, was etwa funktionieren würde, wenn RuntimeException keine Unterklasse von Exception wäre, etwa ein Throwable – aber das haben die Sprachdesigner nicht so modelliert.

Wenn main(String[]) alles weiterleitet

Ist die Ausnahmebehandlung in einem Hauptprogramm ganz egal, so können wir alle Ausnahmen auch an die Laufzeitumgebung weiterleiten, die dann das Programm – genau genommen den Thread – im Fehlerfall abbricht:

Listing 9.9     src/main/java/com/tutego/insel/exception/IDontCare.java, main()

public static void main( String[] args ) throws Exception {

Scanner in = new Scanner( Paths.get( "lyrics.txt" ) );

System.out.println( in.nextLine() );

}

Das funktioniert, da alle Ausnahmen von der Klasse Exception[ 184 ](Genauer gesagt, sind alle Ausnahmen in Java von der Exception-Oberklasse Throwable abgeleitet. ) abgeleitet sind. Wird die Ausnahme nirgendwo sonst aufgefangen, erfolgt die Ausgabe einer Laufzeitfehlermeldung, denn das Exception-Objekt ist beim Interpreter, also bei der virtuellen Maschine, auf der äußersten Aufrufebene gelandet. Natürlich ist das kein guter Stil (obwohl es auch in diesem Buch so gemacht wird, um Programme kurz zu halten), denn Ausnahmen sollten in jedem Fall behandelt werden.

 

Zum Seitenanfang

9.3.8    Bekannte RuntimeException-Klassen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Java-API bietet insgesamt eine große Anzahl von RuntimeException-Klassen, und es werden immer mehr. Tabelle 9.2 listet einige bekannte Ausnahmetypen auf und zeigt, welche Operationen die Ausnahmen auslösen. Wir greifen hier schon auf spezielle APIs vor, die erst später im Buch vorgestellt werden.

Unterklasse von RuntimeException

was den Fehler auslöst

ArithmeticException

ganzzahlige Division durch 0

ArrayIndexOutOfBoundsException

Indexgrenzen wurden missachtet, etwa durch (new int[0])[1]. Eine ArrayIndexOutOfBoundsException ist neben der StringIndexOutOfBoundsException eine Unterklasse von IndexOutOfBoundsException.

ClassCastException

Eine Typumwandlung ist zur Laufzeit nicht möglich. So löst String s = (String) new Object(); eine ClassCastException mit der Meldung »java.lang. Object cannot be cast to java.lang.String« aus.

EmptyStackException

Der Stapelspeicher ist leer. new java.util.Stack(). pop() provoziert den Fehler.

IllegalArgumentException

Eine häufig verwendete Ausnahme, mit der Methoden falsche Argumente melden. Integer.parseInt("tutego") löst eine NumberFormatException, eine Unterklasse von IllegalArgumentException, aus.

IllegalMonitorStateException

Ein Thread möchte warten, hat aber den Monitor nicht. Ein Beispiel: new String().wait();

NullPointerException

Meldet einen der häufigsten Programmierfehler, beispielsweise durch ((String) null).length().

UnsupportedOperationException

Operationen sind nicht gestattet, etwa durch java.util.Arrays.asList(args).add("jv").

Tabelle 9.2     »RuntimeException«-Klassen

 

Zum Seitenanfang

9.3.9    Kann man abfangen, muss man aber nicht Zur vorigen ÜberschriftZur nächsten Überschrift

Eine RuntimeException kann im Code abgefangen werden, muss es aber nicht. Da der Compiler nicht auf einem Abfangen besteht, heißen die aus RuntimeException hervorgegangenen Ausnahmen auch ungeprüfte Ausnahmen bzw. nichtgeprüfte Ausnahmen (engl. unchecked exceptions), und alle übrigen heißen geprüfte Ausnahmen (engl. checked exceptions). Auch muss eine RuntimeException nicht unbedingt bei throws in der Methodensignatur angegeben werden, obwohl einige Autoren das zur Dokumentation machen.

Praktisch ist, dass eine ungeprüfte Ausnahme entlang der Kette von Methodenaufrufen wie eine Blase (engl. bubble) nach oben steigen und irgendwann von einem Block abgefangen werden kann, der sich darum kümmert. Tritt eine RuntimeException zur Laufzeit auf und kommt nicht irgendwann in der Aufrufhierarchie ein try-catch, beendet die JVM den ausführenden Thread. Löst also eine in main(…) aufgerufene Aktion eine RuntimeException aus, ist das das Ende für dieses Hauptprogramm.

[+]  Style

Eine RuntimeException wird nicht im Methodenkopf angegeben, sollte aber im Javadoc dokumentiert werden.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Spring Boot 3 und Spring Framework 6

Spring Boot 3 und Spring Framework 6




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2024

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern