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    Ausnahmen müssen sein Zur vorigen ÜberschriftZur nächsten Überschrift

Warum nicht Ausnahmen zur Regel machen, wenn Ausnahmen die Regel bestätigen?

– Georg-Wilhelm Exler

Programmfehler sind unvermeidlich; Eingaben könnten falsch sein, Dateien könnten verschwinden, Netzwerkverbindungen zusammenbrechen. Eine besondere Herausforderung sind die unerwarteten Fehler – Java bietet die elegante Methode der Exceptions, um Ausnahmen abzufangen, sodass sich Programme aus fast jeder Situation wieder retten können.

In den frühen Programmiersprachen gab es für Routinen keine andere Möglichkeit, als über den Rückgabewert einen Fehlschlag anzuzeigen – in der Programmiersprache C ist das auch heute noch der Fall. Damit gibt es zwei Probleme:

  • Der Fehlercode ist häufig ein »magischer« Wert wie -1, aber auch NULL oder 0. Allerdings kann die Null auch Korrektheit anzeigen. Irgendwie ist das willkürlich. Die Abfrage dieser Werte ist zwingend nötig, und es könnte sich eine Annahme einschleichen wie: »Das wird immer gelingen, ein Fehler ist unmöglich.« Wenn das Programm diesen Fehler dann nicht erkennt und weitermacht, kann das zu bösen Überraschungen führen.

  • Abfragen der Rückgabewerte unterbrechen den Programmfluss unangenehm, zumal der Rückgabewert, wenn er nicht gerade einen Fehler anzeigt, weiterverwendet wird. Der Rückgabewert ist also im weitesten Sinne überladen, da er zwei Zustände anzeigt. Häufig entstehen mit den Fehlerabfragen kaskadierte if-Abfragen, die den Quellcode schwer lesbar machen. Dann wandert der eigentliche Algorithmus im Code immer weiter nach rechts.

[zB]  Beispiel

Die Java-Bibliothek geht bei den Methoden delete(), mkdir(), mkdirs() und renameTo(…) der Klasse File nicht mit gutem Beispiel voran. Anstatt über eine Ausnahme anzuzeigen, dass die Operation nicht geglückt ist, liefern die genannten Methoden false. Das ist unglücklich, denn viele Entwickler verzichten auf den Test, und so entstehen Fehler, die später schwer zu finden sind.

 

Zum Seitenanfang

9.1    Problembereiche einzäunen Zur vorigen ÜberschriftZur nächsten Überschrift

Bei der Verwendung von Exceptions wird der Programmcode nicht durch Abfrage des Rückgabestatus unterbrochen. Ein besonders ausgezeichnetes Programmstück überwacht die ordentliche Ausführung und ruft im Fehlerfall speziellen Programmcode zur Behandlung auf.

 

Zum Seitenanfang

9.1.1    Exceptions in Java mit try und catch Zur vorigen ÜberschriftZur nächsten Überschrift

Den überwachten Programmbereich (Block) leitet das Schlüsselwort try ein. Dem try-Block folgt in der Regel[ 182 ](In manchen Fällen auch ein finally-Block, sodass es dann ein try-finally wird. ) ein catch-Block, in dem Programmcode steht, der die Ausnahme behandelt. Kurz skizziert, sieht das so aus:

try {

// Programmcode, der eine Ausnahme ausführen kann

}

catch ( ... ) {

// Programmcode zum Behandeln der Ausnahme

}

// Es geht ganz normal weiter, denn die Ausnahme wurde behandelt

Fehler führen zu Ausnahmen, und diese Ausnahmen behandelt ein catch-Block. Hinter catch folgt der Programmblock, der beim Auftreten einer Ausnahme ausgeführt wird, um den Fehler abzufangen (daher der Ausdruck catch). Der Fehler kann behandelt, auf der Kommandozeile gemeldet oder etwa in einen Logger geschrieben werden. Ob es zur Laufzeit wirklich zu einem Fehler kommt, ist nicht bekannt, aber wenn, dann ist eine Behandlung vorhanden.

Es ist nach der Fehlerbehandlung nicht mehr so einfach möglich, an der Stelle fortzufahren, an der der Fehler auftrat. Auch lässt sich im Nachhinein nicht wirklich feststellen, an welcher Stelle genau der Fehler aufgetreten ist, wenn es in einem großen try-Block mehrere ausnahmenauslösende Stellen gibt. Andere Programmiersprachen erlauben das durchaus.

Spielfeld für den Fehlerteufel: catch-Blöcke

Abbildung 9.1     Spielfeld für den Fehlerteufel: catch-Blöcke

 

Zum Seitenanfang

9.1.2    Geprüfte und ungeprüfte Ausnahmen Zur vorigen ÜberschriftZur nächsten Überschrift

Java unterscheidet zwischen zwei Gruppen von Ausnahmen: geprüften und ungeprüften Ausnahmen.

  • Die geprüften Ausnahmen müssen zwingend behandelt werden, indem sie entweder aufgefangen oder weitergeleitet werden. Sie können von Methoden oder Konstruktoren im Fehlerfall ausgelöst werden und finden sich der Regel bei Ein-/Ausgabeoperationen.

  • Ungeprüfte Ausnahmen müssen nicht unbedingt aufgefangen werden. Treten die Ausnahmen jedoch auf und werden sie nicht aufgefangen, so führt das zum Ende des ausführenden Threads.

Vorkommen von ungeprüften Ausnahmen (RuntimeException) in der Java-Bibliothek

Einige Arten von Ausnahmen können potenziell an vielen Programmstellen auftreten, etwa eine ganzzahlige Division durch null[ 183 ](Fließkommadivisionen durch 0,0 ergeben entweder ± unendlich oder NaN. ) oder ungültige Indexwerte beim Zugriff auf Array-Elemente. Treten solche Ausnahmen beim Programmlauf auf, liegt ihnen in der Regel ein Denkfehler des Programmierers zugrunde, und das Programm sollte normalerweise nicht versuchen, die ausgelöste Ausnahme aufzufangen und zu behandeln. Daher gibt es in der Java-API mit der Klasse RuntimeException eine Unterklasse von Exception, die Programmierfehler aufzeigt, die behoben werden müssen. (Der Name »RuntimeException« ist jedoch seltsam gewählt, da alle Ausnahmen immer zur Runtime, also zur Laufzeit, erzeugt, ausgelöst und behandelt werden. Doch drückt er aus, dass der Compiler sich für diese Ausnahmen nicht interessiert, sondern erst die JVM zur Laufzeit.)

[»]  Hinweis

Es funktioniert gut, eine RuntimeException als Selbst-schuld-Fehler zu sehen. Durch sachgemäße Prüfung, z. B. der Wertebereiche, würden viele RuntimeExceptions nicht entstehen.

 

Zum Seitenanfang

9.1.3    Eine NumberFormatException fliegt (ungeprüfte Ausnahme) Zur vorigen ÜberschriftZur nächsten Überschrift

Über die Methode Integer.parseInt(…) haben wir an verschiedenen Stellen schon gesprochen. Sie konvertiert eine Zahl, die als Zeichenkette gegeben ist, in eine Dezimalzahl:

int vatRate = Integer.parseInt( "19" );

In diesem Beispiel ist eine Konvertierung möglich, und die Methode führt die Umwandlung ohne Ausnahme aus. Anders sieht das aus, wenn der String keine Zahl repräsentiert:

Listing 9.1     src/main/java/com/tutego/insel/exception/MissNumberFormatException.java

package com.tutego.insel.exception;           /* 1 */

public class MissNumberFormatException { /* 2 */

public static int getVatRate() { /* 3 */

return Integer.parseInt( "19%" ); /* 4 */

} /* 5 */

public static void main( String[] args ) { /* 6 */

System.out.println( getVatRate() ); /* 7 */

} /* 8 */

} /* 9 */

Die Ausführung des Programms bricht mit einer Ausnahme ab, und die virtuelle Maschine gibt uns automatisch eine Meldung aus:

Exception in thread "main" 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.MissNumberFormatException.getVatRate(MissNumberFormatException.java:4)

at e.t.i.e.MissNumberFormatException.main(MissNumberFormatException.java:7)

inline image  In der ersten Zeile sehen wir, dass eine java.lang.NumberFormatException ausgelöst wurde. In der letzten Zeile steht, welche Stelle in unserem Programm zu der Ausnahme führte. (Fehlerausgaben wie diese haben wir schon im Abschnitt »Auf null geht nix, nur die NullPointerException« in Abschnitt 3.7.1, »null-Referenz und die Frage der Philosophie«, beobachtet.)

Eine IDE zeigt eine Exception im Ausgabefenster in Rot an. Praktischerweise verhalten sich die Fehlermeldungen wie Hyperlinks: Ein Klick, und die IDE zeigt die Zeile, die die Exception auslöst. Screenshot IntelliJ, in Eclipse ist es ähnlich.

Abbildung 9.2     Eine IDE zeigt eine Exception im Ausgabefenster in Rot an. Praktischerweise verhalten sich die Fehlermeldungen wie Hyperlinks: Ein Klick, und die IDE zeigt die Zeile, die die Exception auslöst. Screenshot IntelliJ, in Eclipse ist es ähnlich.

Eine NumberFormatException auffangen

Dass ein Programm einfach so abbricht und die JVM endet, ist üblicherweise keine Lösung. Ausnahmen sollten aufgefangen und gemeldet werden. Um Ausnahmen aufzufangen, ist es erst einmal wichtig, zu wissen, was genau für eine Ausnahme ausgelöst wird. In unserem Fall ist das einfach abzulesen, denn die Ausnahme ist ja schon aufgetaucht und klar einem Grund zuzuordnen. Die Java-Dokumentation nennt diese Ausnahme auch, und weil ohne die aufgefangene Ausnahme das Programm abbricht, soll nun die NumberFormatException aufgefangen werden. Dabei kommt wieder die try-catch-Konstruktion zum Einsatz:

Listing 9.2     src/main/java/com/tutego/insel/exception/CatchNumberFormatException.java, main()

String stringToConvert = "19%";

double vat = 19;

try {

vat = Integer.parseInt( stringToConvert );

}

catch ( NumberFormatException e ) {

System.err.printf( "'%s' kann man nicht in eine Zahl konvertieren!%n",

stringToConvert );

}

System.out.printf( "Weiter geht's mit MwSt=%g%n", vat );

Die gesamte Ausgabe ist:

'19%' kann man nicht in eine Zahl konvertieren!

Weiter geht's mit MwSt=19,0000

Integer.parseInt("19%") führt, da der String keine Zahl ist, zu einer NumberFormatException, die wir behandeln, und danach geht es mit der Konsolenausgabe weiter.

Der try-Anweisung folgt ein Block, genannt try-Block. Wir nutzen ihn in Kombination mit einer catch-Klausel. Der Code catch(NumberFormatException e) deklariert einen Exception-Handler – er fängt alles auf, was vom Ausnahmetyp NumberFormatException ist. Die Variable e ist ein Exception-Parameter. Die Nutzung von var ist nicht erlaubt. Da Ausnahmen Objekte sind, referenziert die Variable e dieses Ausnahmeobjekt.

 

Zum Seitenanfang

9.1.4    UUID in Textdatei anhängen (geprüfte Ausnahme) Zur vorigen ÜberschriftZur nächsten Überschrift

Wir wollen ein zweites Programm mit Ausnahmebehandlung schreiben, das eine UUID – einen 128 Bit langen Universally Unique Identifier – in Textform an eine Textdatei anhängt. Die UUID lässt sich einfach über die Klasse UUID ermitteln. In Dateien können wir mit der Methode Files.writeString(…) schreiben – die Methode ist Bestandteil der Files-Klasse seit Java 11.

Der relevanten Zeilen:

String content = UUID.randomUUID() + "\n";

Files.writeString( Path.of( "uuids.txt" ),

content, StandardOpenOption.APPEND );

Die Zeilen können so nicht übersetzt werden, und es gibt einen Compilerfehler. Der Grund ist Files.writeString(…), denn diese Methode kann eine IOException auslösen. IOException ist eine geprüfte Ausnahme, das heißt, wir müssen uns der Tatsache stellen, dass es knallen könnte, und uns für diesen Fall wappnen.

Dokumentierte Ausnahmen in der Javadoc

Eine Ausnahme kommt nicht wirklich überraschend, und Entwickler müssen sich darauf vorbereiten, dass, wenn sie etwas Falsches an Methoden oder Konstruktoren übergeben, diese schimpfen. Im besten Fall erklärt die API-Dokumentation, welche Eingaben gültig sind und welche nicht. Zur »Schnittstelle« einer Methode gehört auch das Verhalten im Fehlerfall. Die API-Dokumentation sollte genau beschreiben, welche Ausnahme – oder Reaktion wie spezielle Rückgabewerte – zu erwarten ist, wenn die Methode ungültige Werte erhält. Die Java-Dokumentation bei Files.writeString(…) macht das, siehe Abbildung 9.3:

Die Javadoc dokumentiert alle möglichen Ausnahmen.

Abbildung 9.3     Die Javadoc dokumentiert alle möglichen Ausnahmen.

Die Beschreibung »IOException – if an I/O error occurs writing to or creating the file, or the text cannot be encoded using the specified charset« ist vielleicht ein bisschen vage, denn sie erklärt nicht, warum genau der Fehler auftrat. Aber es nützt nichts, wir müssen den Fehler behandeln. Wir sehen auch, dass IOException nicht der einzige Fehler ist, der in der Liste steht. Es könnte auch zu einer IllegalArgumentException, UnsupportedOperationException oder SecurityException kommen, doch hier zwingt uns keiner, diese aufzufangen. Das ist genau der Unterschied zwischen einer geprüften und einer ungeprüften Ausnahme.

try-catch-Behandlung

Da Files.writeString(…) eine IOException auslösen kann und das eine geprüfte Ausnahme ist, die behandelt werden muss, gibt es zwei Lösungen: erstens mit try-catch den Fehler behandeln oder zweitens mit throws den Fehler an die Aufrufstelle weiterleiten.

Lösen wir das Problem in unserem Programm mit der IOException zunächst durch eine direkte try-catch-Behandlung:

Listing 9.3     src/main/java/com/tutego/insel/exception/UuidWriter.java, Ausschnitt

public class UuidWriter {

public static void writeUuid() {

String content = UUID.randomUUID().toString();

try {

Files.writeString( Path.of( "uuids.txt" ),

content, StandardOpenOption.APPEND );

}

catch ( IOException e ) {

e.printStackTrace();

}

}



public static void main( String[] args ) {

writeUuid();

}

}

Nach dem Auffangen ist der Fehler wie weggeblasen, und alles geht ganz normal weiter.

Stack-Trace

Die virtuelle Maschine merkt sich auf einem Stapel, welche Methode welche andere Methode aufgerufen hat. Dies nennt sich Stack-Trace. Wenn also die statische main(…)-Methode die Methode writeUuid() aufruft und diese wiederum writeString(…), so sieht der Stapel zum Zeitpunkt von writeString(…) so aus:

writeString

writeUuid

main

Ein Stack-Trace ist im Fehlerfall nützlich, da wir in ihm ablesen können, dass writeString(…) die Ausnahme ausgelöst hat und nicht irgendeine andere Methode.

Oftmals werden im Programm Stack-Traces geloggt. Hierbei hilft eine Methode, die schon im catch-Block steht: e.printStackTrace(). Sie setzt den Stack-Trace standardmäßig auf den System.err-Kanal.

 

Zum Seitenanfang

9.1.5    Wiederholung abgebrochener Bereiche * Zur vorigen ÜberschriftZur nächsten Überschrift

Es gibt in Java bei Ausnahmen bisher keine von der Sprache unterstützte Möglichkeit, an den Punkt zurückzukehren, der die Ausnahme ausgelöst hat. Das ist aber oft erwünscht, etwa dann, wenn eine fehlerhafte Eingabe zu wiederholen ist.

Wir werden auf der Konsole nach einem String fragen und versuchen, diesen in eine Zahl zu konvertieren. Dabei kann natürlich etwas schiefgehen. Wenn ein Benutzer eine Zeichenkette eingibt, die keine Zahl repräsentiert, löst parseInt(…) eine NumberFormatException aus. Wir wollen in diesem Fall die Eingabe wiederholen:

Listing 9.4     src/main/java/com/tutego/insel/exception/ContinueInput.java, main()

int number;

while ( true ) {

try {

System.out.println( "Bitte Zahl eingeben:" );

String input = new java.util.Scanner( System.in ).nextLine();

number = Integer.parseInt( input );

break;

}

catch ( NumberFormatException e ) {

System.err.println( "Das war keine Zahl!" );

}

}

System.out.printf( "%d ist eine %s Zahl%n", number,

Math.random() > 0.5 ? "heiße" : "lahme" );

Die gewählte Lösung ist einfach: Wir programmieren den gesamten Teil in einer Endlosschleife. Geht die problematische Stelle ohne Ausnahme durch, so beenden wir die Schleife mit break. Kommt es zu einer NumberFormatException, dann wird break nicht ausgeführt, und der Programmfluss führt wieder in die Endlosschleife.

Im Übrigen würde auch new java.util.Scanner(System.in).nextInt() unsere Ganzzahl einlesen können, nur wenn die Methode keine Zahl bekommt, löst sie eine InputMismatchException aus – auch das ist eine ungeprüfte Ausnahme.

 

Zum Seitenanfang

9.1.6    Bitte nicht schlucken – leere catch-Blöcke Zur vorigen ÜberschriftZur nächsten Überschrift

Java schreibt vor, dass Ausnahmen in einem catch behandelt (oder nach oben geleitet) werden, aber nicht, was in catch-Blöcken zu geschehen hat. Sie können eine sinnvolle Behandlung beinhalten oder auch einfach leer sein. Ein leerer catch-Block ist in der Regel wenig sinnvoll, weil dann die Ausnahme klammheimlich unterdrückt wird. (Das wäre genauso wie ignorierte Statusrückgabewerte von C-Funktionen.)

Das Mindeste ist eine minimale Fehlerausgabe via System.err.println(e) oder das informativere e.printStackTrace(…) für eine Exception e oder das Loggen dieser Ausnahme. Noch besser ist das aktive Reagieren, denn die Ausgabe selbst behandelt diese Ausnahme nicht! Im catch-Block ist es durchaus legitim, wiederum andere Ausnahmen auszulösen und somit die Ausnahme umzuformen und nach oben weiterzureichen.

[»]  Hinweis *

Wenn wie bei einem Thread.sleep(…) die InterruptedException wirklich egal ist, kann natürlich auch der Block leer sein, doch gibt es dafür nicht so viele sinnvolle Beispiele.

 

Zum Seitenanfang

9.1.7    Mehrere Ausnahmen auffangen Zur vorigen ÜberschriftZur nächsten Überschrift

In einem Programmblock kann es mehrere Stellen geben, die eine Ausnahme auslösen. In den folgenden Zeilen wird zweimal etwas geschrieben:

try {

Path p = Path.of( "uuids.txt" );

Files.writeString( p, UUID.randomUUID() + "\n", StandardOpenOption.APPEND );

Files.writeString( p, UUID.randomUUID() + "\n", StandardOpenOption.APPEND );

}

catch ( IOException e ) { ... }

Lösen mehrere Stellen den gleichen Ausnahmetyp aus, so lässt sich in der Ausnahmebehandlung später nicht mehr so einfach nachvollziehen, welche der beiden Anweisungen zur Ausnahme geführt hat. Wenn das wichtig ist, sollten zwei getrennte try-Blöcke eingesetzt werden, entweder hintereinander oder ineinander geschachtelt.

Anders sieht es aus, wenn unterschiedliche Ausnahmen ausgelöst werden, etwa eine IOException auf der einen Seite und zum Beispiel eine NumberFormatException auf der anderen Seite.

Jetzt gibt es mehrere Möglichkeiten:

  • Alle Ausnahmen werden aufgefangen.

  • Einige Ausnahmen werden aufgefangen, andere werden an den Aufrufer weitergeleitet.

  • Alle Ausnahmen werden nach oben weitergeleitet.

Ein Beispiel: Von der Kommandozeile soll eine Ganzzahl eingelesen und genauso viele UUIDs sollen in eine Textdatei geschrieben werden:

Listing 9.5     src/main/java/com/tutego/insel/exception/UserInputUuidWriter.java, Ausschnitt

Path p = Path.of( "uuids.txt" );

try {

int count = new Scanner( System.in ).nextInt();

for ( int i = 0; i < count; i++ ) {

Files.writeString( p, UUID.randomUUID() + "\n",

StandardOpenOption.APPEND );

}

}

catch ( InputMismatchException e ) {

System.err.println( "Eingabe war keine Ganzzahl" );

}

catch ( IOException e ) {

System.err.println( "Fehler beim Schreiben in die Datei" );

}

IOException ist eine geprüfte Ausnahme und muss zwingend behandelt werden. InputMismatchException ist eine ungeprüfte Ausnahme und müsste nicht unbedingt behandelt werden, doch wenn dann als Eingabe etwa 12ABC geparst wird, crasht das Programm mit einem Strack-Trace, und das wollen wir vermeiden. Auch beim Schreiben in die Datei kann es Ausnahmen geben. Wir müssen uns diesen potenziellen Problemen stellen und daher die Problemzonen in einen try-Block schreiben.

Kommt es dann im try-Block zu einer Ausnahme, fängt der catch-Teil sie ab. Im Code lässt sich ablesen, dass einem try-Block mehrere catch-Klauseln zugeordnet sein können, die verschiedene Typen von Ausnahmen auffangen.

 

Zum Seitenanfang

9.1.8    Zusammenfassen gleicher catch-Blöcke mit dem multi-catch Zur vorigen ÜberschriftZur nächsten Überschrift

Greift ein Programm auf Teile zurück, die scheitern können, so ergeben sich in komplexeren Abläufen schnell Situationen, in denen unterschiedliche Ausnahmen auftreten können. Wir sollten tendenziell versuchen, den Programmcode in einem großen try-Block zu schreiben und dann in catch-Blöcken auf alle möglichen Ausnahmen zu reagieren, die den Block vom vollständigen Durchlaufen abgehalten haben.

Oftmals kommt es zu dem Phänomen, dass die aufgerufenen Programmteile unterschiedliche Ausnahmetypen auslösen, aber die Behandlung der Ausnahmen gleich aussieht. Um Quellcode-Duplizierung zu vermeiden, sollte der Programmcode zusammengefasst werden. In Java können mehrere Ausnahmen gleich behandelt werden; diese Schreibweise heißt multi-catch. In der abgewandelten Variante von catch steht dann nicht mehr nur eine Ausnahme, sondern eine Sammlung von Ausnahmen, die ein | trennt. Der senkrechte Strich ist schon als Oder-Operator bekannt und wurde daher auch hier eingesetzt, denn die Ausnahmen sind ja auch als eine Oder-Verknüpfung zu verstehen.

Listing 9.6     src/main/java/com/tutego/insel/exception/UserInputUuidWriterMultiCatch.java, Ausschnitt

Path p = Path.of( "uuids.txt" );

try {

int count = new Scanner( System.in ).nextInt();

for ( int i = 0; i < count; i++ ) {

Files.writeString( p, UUID.randomUUID() + "\n",

StandardOpenOption.APPEND );

}

}

catch ( InputMismatchException | IOException e ) {

System.err.println( "Programmfehler" );

e.printStackTrace();

}

Die Exception-Variable ist implizit final.

 


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