Rheinwerk Computing < openbook >


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


Download:

- Listings, ca. 2,7 MB


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

Pfeil8 Ausnahmen müssen sein
Pfeil8.1 Problembereiche einzäunen
Pfeil8.1.1 Exceptions in Java mit try und catch
Pfeil8.1.2 Geprüfte und ungeprüfte Ausnahmen
Pfeil8.2 Geprüfte Ausnahmen
Pfeil8.2.1 Letzte ausgeführte Java-Programme loggen
Pfeil8.2.2 try-catch-Behandlung
Pfeil8.2.3 throws im Methodenkopf angeben
Pfeil8.3 Ungeprüfte Ausnahmen (RuntimeException)
Pfeil8.3.1 Eine NumberFormatException fliegt
Pfeil8.3.2 Bekannte RuntimeException-Klassen
Pfeil8.3.3 Kann man abfangen, muss man aber nicht
Pfeil8.4 Gut gefangen
Pfeil8.4.1 Bitte nicht schlucken – leere catch-Blöcke
Pfeil8.4.2 Wiederholung abgebrochener Bereiche *
Pfeil8.4.3 Mehrere Ausnahmen auffangen
Pfeil8.4.4 Ablauf einer Ausnahmesituation
Pfeil8.4.5 Abschlussbehandlung mit finally
Pfeil8.5 Die Klassenhierarchie der Ausnahmen
Pfeil8.5.1 Eigenschaften des Exception-Objekts
Pfeil8.5.2 Basistyp Throwable
Pfeil8.5.3 Die Exception-Hierarchie
Pfeil8.5.4 Oberausnahmen auffangen
Pfeil8.5.5 Schon gefangen?
Pfeil8.5.6 Alles geht als Exception durch
Pfeil8.5.7 Zusammenfassen gleicher catch-Blöcke mit dem multi-catch
Pfeil8.6 Auslösen eigener Exceptions
Pfeil8.6.1 Mit throw Ausnahmen auslösen
Pfeil8.6.2 Vorhandene Runtime-Ausnahmetypen kennen und nutzen
Pfeil8.6.3 Parameter testen und gute Fehlermeldungen
Pfeil8.6.4 Neue Exception-Klassen deklarieren
Pfeil8.6.5 Eigene Ausnahmen als Unterklassen von Exception oder RuntimeException?
Pfeil8.6.6 Ausnahmen abfangen und weiterleiten *
Pfeil8.6.7 Aufruf-Stack von Ausnahmen verändern *
Pfeil8.6.8 Präzises rethrow *
Pfeil8.6.9 Geschachtelte Ausnahmen *
Pfeil8.7 Automatisches Ressourcen-Management (try mit Ressourcen)
Pfeil8.7.1 try mit Ressourcen
Pfeil8.7.2 Die Schnittstelle AutoCloseable
Pfeil8.7.3 Mehrere Ressourcen nutzen
Pfeil8.7.4 try mit Ressourcen auf null-Ressourcen
Pfeil8.7.5 Ausnahmen vom close()
Pfeil8.7.6 Unterdrückte Ausnahmen *
Pfeil8.8 Besonderheiten bei der Ausnahmebehandlung *
Pfeil8.8.1 Rückgabewerte bei ausgelösten Ausnahmen
Pfeil8.8.2 Ausnahmen und Rückgaben verschwinden – das Duo return und finally
Pfeil8.8.3 throws bei überschriebenen Methoden
Pfeil8.8.4 Nicht erreichbare catch-Klauseln
Pfeil8.9 Harte Fehler – Error *
Pfeil8.10 Assertions *
Pfeil8.10.1 Assertions in eigenen Programmen nutzen
Pfeil8.10.2 Assertions aktivieren und Laufzeit-Errors
Pfeil8.10.3 Assertions feiner aktivieren oder deaktivieren
Pfeil8.11 Zum Weiterlesen
 

Zum Seitenanfang

8.8    Besonderheiten bei der Ausnahmebehandlung * Zur vorigen ÜberschriftZur nächsten Überschrift

Bei der Ausnahmebehandlung gibt es ein paar Überraschungen, die in den folgenden vier Unterabschnitten gesondert vorgestellt werden.

 

Zum Seitenanfang

8.8.1    Rückgabewerte bei ausgelösten Ausnahmen Zur vorigen ÜberschriftZur nächsten Überschrift

Verspricht die Methode eine Rückgabe, so prüft der Compiler, ob jeder Programmfluss zu einem Rückgabewert führt. Doch die Aussage »Jede Methode mit einem Ergebnistyp ungleich void muss eine return-Anweisung besitzen« müssen wir etwas relativieren. Nur in einem speziellen Fall muss die Methode keine return-Anweisung besitzen, nämlich genau dann, wenn vor dem Ende der Methode eine throw-Anweisung die Abarbeitung beendet:

class Windows10KeyGenerator {

public String generateKey() {

throw new UnsupportedOperationException();

}

}

Ein Blick auf generateKey() verrät, dass trotz eines angekündigten Rückgabewerts keine return-Anweisung im Rumpf steht. Die Abarbeitung wird vor dem Rücksprung durch eine Exception abgebrochen. Kann der Compiler sehen, dass eine Methode eine Ausnahme auslöst und die return-Anweisung nicht erreichbar ist, dann ist alles hinter dem throw nicht erreichbar und es dürfen keine weiteren Anweisungen folgen.

generateKey() muss diese Exception nicht mit throws ankündigen, da UnsupportedOperationException eine RuntimeException ist.

 

Zum Seitenanfang

8.8.2    Ausnahmen und Rückgaben verschwinden – das Duo return und finally Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Phänomen in der Ausnahmebehandlung von Java ist eine return-Anweisung innerhalb eines finally-Blocks. Zunächst einmal »überschreibt« ein return im finally-Block den Rückgabewert eines return im try-Block:

static String getIsbn() {

try {

return "3821829877";

}

finally {

return "";

}

}

Der Aufrufer empfängt immer einen leeren String.

Interessant ist auch folgendes Programm:

public static int a() {

while ( true ) {

try {

return 0;

}

finally {

break;

}

}



return 1;

}

Die Ausgabe auf der Konsole ist 1. Das break im finally lässt die Laufzeitumgebung aus der Schleife austreten und den Rückgabewert ignorieren.

Ein weiteres Kuriosum sind Ausnahmen. Die Laufzeitumgebung gibt bei einer return-Anweisung im finally-Block eine im try-Block ausgelöste Ausnahme nicht zum Aufrufer weiter, sondern bietet einfach die Rückgabe an.

Die folgende Methode löst zum Beispiel eine RuntimeException aus, die aber der Aufrufer der Methode nie sieht:

static void hillaryVsDonald() {

try {

throw new RuntimeException();

}

finally {

return;

}

}

Entfernen wir die Zeile mit dem return, ist das Verhalten der Laufzeitumgebung so wie erwartet.

inline image  Der Java-Compiler von Eclipse markiert die Diskrepanz und zeigt eine Warnung an (»finally block does not complete normally«). Mit der Annotation @SuppressWarnings("finally") schalten wir diesen Hinweis ab.

 

Zum Seitenanfang

8.8.3    throws bei überschriebenen Methoden Zur vorigen ÜberschriftZur nächsten Überschrift

Beim Überschreiben von Methoden gibt es eine wichtige Regel: Überschriebene Methoden in einer Unterklasse dürfen nicht mehr Ausnahmen auslösen, als schon beim throws-Teil der Oberklasse aufgeführt sind. Da das gegen das Substitutionsprinzip verstieße, kann eine Methode der Unterklasse nur

  • dieselben Ausnahmen wie die Oberklasse auslösen,

  • Ausnahmen spezialisieren oder

  • weglassen.

Dazu sehen wir hier ein konstruiertes Beispiel für die beiden letzten Fälle:

Listing 8.31    src/main/java/com/tutego/insel/exception/SubRandomAccessFile.java, Ausschnitt

public class SubRandomAccessFile extends RandomAccessFile {

public SubRandomAccessFile( File file, String mode ) throws FileNotFoundException {

super( file, mode );

}

@Override

public long length() {

try {

return super.length();

}

catch ( IOException e ) {

return 0;

}

}

@Override

public void write( int b ) throws ProtocolException {

try {

super.write( b );

}

catch ( IOException e ) {

throw new ProtocolException();

}

}



@Override

public void close() {

}

}

Die Methoden length(), write(…) und close() lösen in RandomAccessFile eine IOException aus. Unsere Unterklasse SubRandomAccessFile überschreibt length() und lässt die Ausnahme in der Signatur weg. Das hat in der Nutzung einige Folgen, denn wenn wir die Klasse als SubRandomAccessFile der Art

SubRandomAccessFile raf = ...

raf.length();

verwenden, muss bei length() keine Ausnahme mehr abgefangen werden – und darf auch gar nicht abgefangen werden, weil ein try-catch auf eine IOException zu einem Compilerfehler führt.

Umgekehrt: Ist raf vom Typ der Basisklasse RandomAccessFile, muss die Ausnahme auf jeden Fall abgefangen werden:

RandomAccessFile raf = ...;

try {

raf.length();

}

catch ( IOException e ) { }

Das zeigt die Schwierigkeit, bei überschriebenen Methoden die Ausnahmen wegzulassen.

Bei der Methode write(…) führt throws den Ausnahmetyp ProtocolException als Unterklasse von IOException auf. Natürlich reicht es nicht aus, in write(…) einfach super.write(…) stehen zu lassen (was nur eine allgemeinere IOException auslösen würde, aber nicht die versprochene speziellere ProtocolException). Daher fangen wir im Rumpf der Methode das super.write(…) ab und erzeugen die speziellere ProtocolException.

[»]  Design

Wenn demnach eine überschriebene Methode der Unterklasse keine geprüften Ausnahmen hinzufügen kann, muss das Design der Basistypen so entworfen sein, dass Unterklassen notwendige Ausnahmen melden können.

[»]  Hinweis

Implementiert eine Unterklasse einen eigenen Konstruktor und ruft dieser super(…) für einen Konstruktor auf, der eine Ausnahme auslöst, so muss auch der Konstruktor der Unterklasse diese Ausnahme melden, denn der neue Konstruktor kann die Ausnahme nicht auffangen. In unserem Beispiel wäre also Folgendes illegal:

public SubRandomAccessFile( File file, String mode ) {

try {

super( file, mode );

} catch ( Exception e ) { }

}

Der Grund ist ganz einfach: Wenn der Konstruktor der Oberklasse eine Ausnahme auslöst, ist das Objekt nicht vollständig initialisiert. Und wenn der Konstruktor der Unterklasse dann die Ausnahme abfängt, würde ja die Unterklasse vielleicht nicht vollständig initialisierte Eigenschaften der Oberklasse erben, also ein halbgares Objekt. Das ist unerwünscht.

 

Zum Seitenanfang

8.8.4    Nicht erreichbare catch-Klauseln Zur vorigen ÜberschriftZur nächsten Überschrift

Löst in einem try-Block eine Anweisung eine Ausnahme aus und gibt es dafür eine catch-Klausel, so heißt die catch-Klausel erreichbar. Zusätzlich darf vor dieser catch-Klausel natürlich kein anderes catch stehen, das diese Ausnahme mit abfängt. Wenn wir zum Beispiel catch(Exception e) als erstes Auffangbecken bereitstellen, behandelt das natürlich alle Ausnahmen. Die Konsequenz daraus: catch-Klauseln müssen immer von den speziellen zu den allgemeinen Ausnahmearten sortiert werden (alles andere würde der Compiler auch verhindern).

Wenn wir ein Objekt RandomAccessFile aufbauen und anschließend readLine() verwenden, so muss eine FileNotFoundException vom Konstruktor und eine IOException von readLine() abgefangen werden. Da eine FileNotFoundException eine Spezialisierung ist, also eine Unterklasse von IOException, würde ein catch(IOException e) schon reichen. Steht im Quellcode folglich der catch für die FileNotFoundException dahinter, wird der Teil nie ausgeführt werden können, und der Compiler merkt das zu Recht an.

Übertriebene throws-Klauseln

Eine Methode compiliert, auch wenn sie zu viele oder zu allgemeine Ausnahmen in ihrer throws-Klausel angibt:

Listing 8.32    src/main/java/com/tutego/insel/exception/TooManyExceptions.java, openFile()

void openFile() throws FileNotFoundException,

IOException,

InterruptedException {

try ( RandomAccessFile r = new RandomAccessFile( "", "" ) ) { }

}

Unsere Methode openFile() ruft den Konstruktor von RandomAccessFile auf, was bekannterweise zu einer FileNotFoundException führen kann. openFile() jedoch gibt neben FileNotFoundException noch die allgemeinere Oberklasse IOException an und meldet mit InterruptedException noch eine geprüfte Ausnahme, die der Rumpf überhaupt nicht auslöst. Trotzdem lässt der Compiler das durch.

Beim Aufruf solcher Methoden in try-Blöcken müssen in den catch-Klauseln die zu viel deklarierten Exceptions aufgefangen werden, auch wenn sie nicht wirklich erreicht werden können:

Listing 8.33    src/main/java/com/tutego/insel/exception/TooManyExceptions.java, useFile()

try {

openFile();

}

catch ( IOException e ) { }

catch ( InterruptedException e ) { }

Der Sinn besteht darin, dass dies später in einer Erweiterung einer Methode, etwa einer InterruptedException, durchaus vorkommen kann, und dann sind die Aufrufer darauf schon vorbereitet.

 


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: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




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

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

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