Galileo Computing < openbook >Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Exceptions
7 Äußere.innere Klassen
8 Besondere Klassen der Java SE
9 Generics<T>
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Einführung in die nebenläufige Programmierung
13 Einführung in Datenstrukturen und Algorithmen
14 Einführung in grafische Oberflächen
15 Einführung in Dateien und Datenströme
16 Einführung in die <XML>-Verarbeitung mit Java
17 Einführung ins Datenbankmanagement mit JDBC
18 Bits und Bytes und Mathematisches
19 Die Werkzeuge des JDK
A Die Klassenbibliothek
Stichwort

Download:
- openbook, ca. 24,5 MB
- Aufgaben, ca. 1,1 MB
- Programme, ca. 12,8 MB
Buch bestellen
Ihre Meinung?

Spacer
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Galileo Computing
1308 S., 10., aktualisierte Auflage, geb., mit DVD
ca. 49,90 Euro, ISBN 978-3-8362-1802-3
Pfeil6 Exceptions
Pfeil6.1 Problembereiche einzäunen
Pfeil6.1.1 Exceptions in Java mit try und catch
Pfeil6.1.2 Eine NumberFormatException auffangen
Pfeil6.1.3 Ablauf einer Ausnahmesituation
Pfeil6.1.4 Eigenschaften vom Exception-Objekt
Pfeil6.1.5 Wiederholung abgebrochener Bereiche *
Pfeil6.1.6 Mehrere Ausnahmen auffangen
Pfeil6.1.7 throws im Methodenkopf angeben
Pfeil6.1.8 Abschlussbehandlung mit finally
Pfeil6.2 RuntimeException muss nicht aufgefangen werden
Pfeil6.2.1 Beispiele für RuntimeException-Klassen
Pfeil6.2.2 Kann man abfangen, muss man aber nicht
Pfeil6.3 Die Klassenhierarchie der Fehler
Pfeil6.3.1 Die Exception-Hierarchie
Pfeil6.3.2 Oberausnahmen auffangen
Pfeil6.3.3 Schon gefangen?
Pfeil6.3.4 Alles geht als Exception durch
Pfeil6.3.5 Zusammenfassen gleicher catch-Blöcke mit dem multi-catch
Pfeil6.4 Harte Fehler: Error *
Pfeil6.5 Auslösen eigener Exceptions
Pfeil6.5.1 Mit throw Ausnahmen auslösen
Pfeil6.5.2 Vorhandene Runtime-Fehlertypen kennen und nutzen
Pfeil6.5.3 Parameter testen und gute Fehlermeldungen
Pfeil6.5.4 Neue Exception-Klassen deklarieren
Pfeil6.5.5 Eigene Ausnahmen als Unterklassen von Exception oder RuntimeException?
Pfeil6.5.6 Ausnahmen abfangen und weiterleiten *
Pfeil6.5.7 Aufrufstack von Ausnahmen verändern *
Pfeil6.5.8 Präzises rethrow *
Pfeil6.5.9 Geschachtelte Ausnahmen *
Pfeil6.6 Automatisches Ressourcen-Management (try mit Ressourcen)
Pfeil6.6.1 try mit Ressourcen
Pfeil6.6.2 Ausnahmen vom close() bleiben bestehen
Pfeil6.6.3 Die Schnittstelle AutoCloseable
Pfeil6.6.4 Mehrere Ressourcen nutzen
Pfeil6.6.5 Unterdrückte Ausnahmen *
Pfeil6.7 Besonderheiten bei der Ausnahmebehandlung *
Pfeil6.7.1 Rückgabewerte bei ausgelösten Ausnahmen
Pfeil6.7.2 Ausnahmen und Rückgaben verschwinden: Das Duo »return« und »finally«
Pfeil6.7.3 throws bei überschriebenen Methoden
Pfeil6.7.4 Nicht erreichbare catch-Klauseln
Pfeil6.8 Den Stack-Trace erfragen *
Pfeil6.8.1 StackTraceElement
Pfeil6.8.2 printStackTrace()
Pfeil6.8.3 StackTraceElement vom Thread erfragen
Pfeil6.9 Assertions *
Pfeil6.9.1 Assertions in eigenen Programmen nutzen
Pfeil6.9.2 Assertions aktivieren
Pfeil6.10 Zum Weiterlesen

Galileo Computing - Zum Seitenanfang

6.6 Automatisches Ressourcen-Management (try mit Ressourcen)Zur nächsten Überschrift

Java hat eine automatische Garbage-Collection (GC), sodass nicht mehr referenzierte Objekte erkannt und ihr Speicher automatisch freigegeben wird. Nun bezieht sich die GC aber ausschließlich auf Speicher, doch es gibt viele weitere Ressourcen:

  • Dateisystem-Ressourcen von Dateien
  • Netzwerkressourcen wie Socket-Verbindungen
  • Datenbankverbindungen
  • nativ gebundene Ressourcen vom Grafiksubsystem
  • Synchronisationsobjekte

Auch hier gilt es, nach getaner Arbeit aufzuräumen und Ressourcen freizugeben, etwa Dateien und Datenbankverbindungen zu schließen.

Mit dem try-catch-finally-Konstrukt haben wir gesehen, wie Ressourcen freizugeben sind. Doch es lässt sich auch ablesen, dass relativ viel Quellcode geschrieben werden muss und der try-catch-finally drei Unfeinheiten hat:

  1. Sollte eine Variable in finally zugänglich sein, muss sie außerhalb des try-Blocks deklariert werden, was ihr eine höhere Sichtbarkeit als nötig gibt.
  2. Das Schließen der Ressourcen bringt oft ein zusätzliches try-catch mit sich.
  3. Eine im finally ausgelöste Ausnahme (etwa beim close()) überdeckt die im try-Block ausgelöste Ausnahme.

Galileo Computing - Zum Seitenanfang

6.6.1 try mit RessourcenZur nächsten ÜberschriftZur vorigen Überschrift

Um das Schließen von Ressourcen zu vereinfachen, wurde in Java 7 eine besondere Form der try-Anweisung eingeführt, die try mit Ressourcen genannt werden. Mit ihm lassen sich Ressource-Typen, die die Schnittstelle java.lang.AutoCloseable implementieren, automatisch schließen. Ein-/Ausgabeklassen wie Scanner, InputStream und Writer, implementieren diese Schnittstelle und können direkt verwendet werden. Weil try mit Ressourcen dem Automatic Resource Management dient, heißt der spezielle try-Block auch ARM-Block.

Gehen wir in die Praxis: Aus einer Datei soll mit einem Scanner die erste Zeile gelesen und ausgegeben werden. Nach dem Lesen soll der Scanner geschlossen werden. Die linke Seite der folgenden Tabelle nutzt die spezielle Syntax, die rechte Seite ist im Prinzip die Übersetzung, allerdings noch etwas vereinfacht, wie wir später genauer sehen werden.

Tabelle 6.3: Die main()-Methode von TryWithResources1 und ihre prinzipielle Umsetzung

try mit Ressourcen Vereinfachte ausgeschriebene Implementierung

InputStream in =
ClassLoader.getSystemResourceAsStream(
"EastOfJava.txt" );
try ( Scanner res =

new Scanner( in ) )
{
System.out.println(
res.nextLine() );
}

InputStream in =
ClassLoader.getSystemResourceAsStream(
"EastOfJava.txt" );
{
final Scanner res =
new Scanner( file );
try
{
System.out.println(
res.nextLine() );
}
finally
{
res.close();
}
}

Üblicherweise folgt nach dem Schlüsselwort try ein Block, doch try-mit-Ressourcen nutzt eine eigene spezielle Syntax.

  1. Nach dem try folgt statt dem direkten {}-Block eine Anweisung in runden Klammern, und dann erst der {}-Block, also try (...) {...} statt try {...}.
  2. In den runden Klammern findet man eine lokale Variablendeklaration (die Variable ist automatisch final) mit einer Zuweisung. Die Ressourcenvariable muss vom Typ AutoCloseable sein. Rechts vom Gleichheitszeichen steht ein Ausdruck, etwa ein Konstruktor- oder Methodenaufruf. Der Typ des Ausdrucks muss natürlich auch intanceof AutoCloseable sein.

Die in dem try deklarierte lokale AutoCloseable-Variable ist nur in dem Block gültig und wird automatisch freigegeben, gleichgültig ob der ARM-Block korrekt durchlaufen wurde oder ob es bei der Abarbeitung zu einem Fehler kam. Der Compiler fügt alle nötigen Prüfungen ein.


Galileo Computing - Zum Seitenanfang

6.6.2 Ausnahmen vom close() bleiben bestehenZur nächsten ÜberschriftZur vorigen Überschrift

Unser Scanner-Beispiel hat eine Besonderheit, denn keine der Methoden löst eine geprüfte Ausnahme aus – weder getSystemResourceAsStream(), new Scanner(InputStream), nextLine() noch das close(), was try-mit-Ressourcen automatisch aufruft. Anders ist es, wenn die Ressource ein InputStream ist, denn dort deklariert die close()-Methode eine IOException. Die muss daher auch behandelt werden, wie es das folgende Beispiel zeigt:

Listing 6.23: TryWithResourcesReadsLine, readFirstLine()

static String readFirstLine( File file )
{
try ( BufferedReader br = new BufferedReader(new FileReader(file) ) )
{
return br.readLine();
}
catch ( IOException e ) { e.printStackTrace(); return null; }
}

Wenn try-mit-Ressourcen verwendet wird, bleibt die Ausnahme bestehen; es zaubert die Ausnahmen beim catch also nicht weg.

Hinweis

Löst close() eine geprüfte Ausnahme aus, und wird diese nicht behandelt, so kommt
es zum Compilerfehler. Die close()-Methode vom BufferedReader löst zum Beispiel eine IOException aus, sodass sich die folgende Methode nicht übersetzen lässt:

void no()
{
try ( Reader r = new BufferedReader(null) ) { } // Fehler Compilerfehler
}
Der Ausdruck new BufferedReader(null) benötigt keine Behandlung, denn der Konstruktor löst keine Ausnahme aus. Einzig der nicht behandelte Fehler von close() führt zu »exception thrown from implicit call to close() on resource variable 'r'«.


Galileo Computing - Zum Seitenanfang

6.6.3 Die Schnittstelle AutoCloseableZur nächsten ÜberschriftZur vorigen Überschrift

Die ARM-Anweisung schließt Ressourcen vom Typ AutoCloseable. Daher wird es Zeit, sich diese Schnittstelle etwas genauer anzuschauen:

package java.lang;
public interface AutoCloseable
{
void close() throws Exception;
}

Anders als das übliche close() ist die Ausnahme deutlich allgemeiner mit Exception angegeben; die Ein-/Ausgabe-Klassen lösen beim Misslingen immer eine IOException aus, aber jede Klasse hat eigene Ausnahmetypen:

Tabelle 6.4: Einige Typen, die AutoCloseable implementieren

Typ Signatur
java.io.Scanner close() // ohne Ausnahme
javax.sound.sampled.Line close() // ohne Ausnahme
java.io.FileInputStream close() throws IOException
java.sql.Connection close() throws SQLException

Eine Unterklasse darf die Ausnahme ja auch weglassen, das machen Klassen wie der Scanner, der keine Ausnahme weiterleitet, sondern sie intern schluckt – wenn es Ausnahmen gab, liefert sie die Scanner-Methode ioException().

AutoCloseable und Closeable

Auf den ersten Blick einleuchtend wäre es, die schon existierende Schnittstelle Closeable als Typ zu nutzen. Doch das hätte Nachteile: Die close()-Methode ist mit einem throws IOException deklariert, was bei einer allgemeinen automatischen Ressourcen-Freigabe unpassend ist, wenn etwa ein Grafikobjekt bei der Freigabe eine IOException auslöst. Vielmehr ist der Weg anders herum: Closeable erweitert AutoCloseable, denn das Schließen von Ein-/Ausgabe-Ressourcen ist eine besondere Art, allgemeine Ressourcen zu schließen.

package java.io;

import java.io.IOException;

public interface Closeable extends AutoCloseable
{
void close() throws IOException;
}

Wer ist AutoCloseable?

Da alle Klassen, die Closeable implementieren, auch automatisch vom Typ AutoCloseable sind, kommen schon einige Typen zusammen. Im Wesentlichen sind es aber Klassen aus dem java.io-Paket, wie Channel-, Reader-, Writer-Implementierungen, FileLock, XMLDecoder und noch ein paar Exoten wie URLClassLoader, ImageOutputStream. Auch Typen aus dem java.sql-Paket gehören zu den Nutznießern. Klassen aus dem Bereich Threading, wo etwa ein Lock wieder freigeben werden könnte, oder Grafik-Anwendungen, bei denen der Grafik-Kontext wieder freigegeben werden muss, gehören nicht dazu.


Galileo Computing - Zum Seitenanfang

6.6.4 Mehrere Ressourcen nutzenZur nächsten ÜberschriftZur vorigen Überschrift

Unsere beiden Beispiele zeigten die Nutzung eines Ressource-Typs. Es sind aber auch mehrere Typen möglich, die dann mit einem Semikolon getrennt werden:

try ( InputStream  in  = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest) )
{ ... }
Hinweis

Die Trennung erledigt ein Semikolon, und jedes Segment kann einen unterschiedlichen Typ deklarieren, etwa InputStream/OutputStream. Die Ressourcen-Typen müssen also nicht gleich sein, und auch wenn sie es sind, muss der Typ immer neu geschrieben werden, also etwa:

try ( InputStream in1 = ...; InputStream in2 = ... )
Es ist ungültig, Folgendes zu schreiben:
try ( InputStream in1 = ..., in2 = ... )    // Fehler Compilerfehler

Wenn es beim Anlegen in der Kette zu einem Fehler kommt, wird nur das geschlossen, was auch aufgemacht wurde. Wenn es also bei der ersten Initialisierung von in1 schon zu einer Ausnahme kommt, wird die Belegung von in2 erst gar nicht begonnen und daher auch nicht geschlossen. (Intern setzt der Compiler das als geschachtelte try-catch-finally-Blöcke um.)

Beispiel

Am Schluss der Ressourcensammlung kann – muss aber nicht – ein Semikolon stehen, so wie auch bei Feldinitialisierungen zum Schluss ein Komma stehen kann:

int[] array = { 1, 2, };
// ^ Komma optional
try ( InputStream in = new FileInputStream(src); ) { ... }
// ^ Semikolon optional
try ( InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest); ) { ... }
// ^ Semikolon optional
Ob das stilvoll ist, muss jeder selbst entscheiden; in der Insel steht kein unnützes Zeichen.


Galileo Computing - Zum Seitenanfang

6.6.5 Unterdrückte Ausnahmen *Zur nächsten ÜberschriftZur vorigen Überschrift

Aufmerksame Leser haben bestimmt schon ein Detail wahrgenommen: Im Text steht »vereinfachte ausgeschriebene Implementierung«, was vermuten lässt, dass es ganz so einfach doch nicht ist. Das stimmt, denn es können zwei Ausnahmen auftauchen, die einiges an Sonderbehandlung benötigen:

  • Ausnahme im try-Block. An sich unproblematisch.
  • Ausnahme beim close(). Auch an sich unproblematisch. Aber es gibt mehrere close()-Aufrufe, wenn nicht nur eine Ressource verwendet wurde. Ungünstig.
  • Die Steigerung: Ausnahme im try-Block und dann auch noch Ausnahme(n) beim close(). Das ist ein echtes Problem!

Eine Ausnahme alleine ist kein Problem, aber zwei Ausnahmen auf einmal bilden ein großes Problem, da ein Programmblock nur genau eine Ausnahme melden kann und nicht eine Sequenz von Ausnahmen. Daher sind verschiedene Fragen zu klären, falls der try-Block und close() beide eine Ausnahme auslösen:

  • Welche Ausnahme ist wichtiger? Die Ausnahme im try-Block oder die vom close()?
  • Wenn es zu zwei Ausnahmen kommt: Soll die von close() vielleicht immer verdeckt werden und immer nur die vom try-Block zum Anwender kommen?
  • Wenn beide Ausnahmen wichtig sind, wie sollen sie gemeldet werden?

Wie haben sich die Java-Ingenieure entschieden? Eine Ausnahme bei close() darf bei einem gleichzeitigen Auftreten einer Exception im try-Block auf keinen Fall verschwinden.[157](In einem frühen Prototyp war dies tatsächlich der Fall – die Ausnahme wurde komplett geschluckt.) Wie also beide Ausnahmen melden? Hier gibt es einen Trick: Da die Ausnahme im try-Block wichtiger ist, ist sie die »Haupt-Ausnahme«, und die close()-Ausnahme kommt Huckepack als Extra-Information mit oben drauf.

Dieses Verhalten soll das nächste Beispiel zeigen. Um die Ausnahmen besser steuern zu können, soll eine eigene AutoCloseable-Implementierung eine Ausnahme in close() auslösen.

Listing 6.24: SuppressedClosed.java

public class SuppressedClosed
{
public static void main( String[] args )
{
class NotCloseable implements AutoCloseable
{
@Override public void close()
{
throw new UnsupportedOperationException( "close() mag ich nicht" );
}
}

try ( NotCloseable res = new NotCloseable() ) {
throw new NullPointerException();
}
}
}

Das Programm löst also im close() und im try-Block eine Ausnahme aus. Das Resultat ist:

Exception in thread "main" java.lang.NullPointerException
at SuppressedClosed.main(SuppressedClosed.java:14)
Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht
at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9)
at SuppressedClosed.main(SuppressedClosed.java:15)

Die interessante Zeile beginnt mit »Suppressed:«, denn dort ist die close()-Ausnahme referenziert. An den Aufrufer kommt die spannende Ausnahme vom misslungenen try-Block aber nicht direkt von close(), sondern verpackt in der Hauptausnahme und muss extra erfragt werden.

Zum Vergleich: Kommentieren wir throw new NullPointerException() aus, gibt es nur noch die close()-Ausnahme und es folgt auf der Konsole:

Exception in thread "main" java.lang.UnsupportedOperationException: close() mag ich nicht
at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9)
at SuppressedClosed.main(SuppressedClosed.java:15)

Die Ausnahme ist also nicht irgendwo anders untergebracht, sondern die »Hauptausnahme«.

Eine Steigerung ist, dass es mehr als eine Ausnahme beim Schließen geben kann. Simulieren wir auch dies wieder an einem Beispiel, indem wir unser Beispiel um eine Zeile ergänzen:

try ( NotCloseable res1 = new NotCloseable();
NotCloseable res2 = new NotCloseable() )
{
throw new NullPointerException();
}

Aufgerufen führt dies zu:

Exception in thread "main" java.lang.NullPointerException
at SuppressedClosed.main(SuppressedClosed.java:15)
Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht
at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9)
at SuppressedClosed.main(SuppressedClosed.java:16)
Suppressed: java.lang.UnsupportedOperationException: close() mag ich nicht
at SuppressedClosed$1NotCloseable.close(SuppressedClosed.java:9)
at SuppressedClosed.main(SuppressedClosed.java:16)

Jede unterdrückte close()-Ausnahme taucht auf.

Umsetzung

Im Kapitel über finally wurde das Verhalten vorgestellt, dass eine Ausnahme im finally eine Ausnahme im try-Block unterdrückt. Der Compiler setzt bei der Umsetzung vom try-mit-Ressourcen das close() in einen finally-Block. Ausnahmen im finally-Block sollen eine mögliche Hauptausnahme aber nicht schlucken. Daher fängt die Umsetzung vom Compiler jede mögliche Ausnahme im try-Block ab sowie die close()-Ausnahme und hängt diese Schließ-Ausnahme, falls vorhanden, an die Hauptausnahme.

Spezielle Methoden in Throwable *

Damit eine normale Exception die unterdrückten close()-Ausnahmen Huckepack nehmen kann, sind in der Basisklasse Throwable seit Java 7 zwei Methoden hinzugekommen:

final class java.lang.Throwable
  • final Throwable[] getSuppressed()
    Liefert alle unterdrückten Ausnahmen. Die printStackTrace()-Methode zeigt alle unterdrückten Ausnahmen und greift auf getSuppressed() zurück. Für Anwender wird es selten Anwendungsfälle für diese Methode geben.
  • final void addSuppressed(Throwable exception)
    Fügt eine neue unterdrückte Ausnahme hinzu. In der Regel ruft der finally-Block vom try-mit-Ressourcen die Methode auf, doch wir können auch selbst die Methode nutzen, wenn wir mehr als eine Ausnahme melden wollen. Die Java-Bibliothek selbst nutzt das bisher nur an sehr wenigen Stellen.

Neben den beiden Methoden gibt es einen protected-Konstruktor, der bestimmt, ob es überhaupt unterdrückte Ausnahmen geben soll oder ob sie nicht vielleicht komplett geschluckt werden. Wenn, dann zeigt sie auch printStackTrace() nicht mehr an.

Blick über den Tellerrand

In C++ gibt es Dekonstruktoren, die beliebige Anweisungen ausführen, wenn ein Objekt freigegeben wird. Hier lässt sich auch das Schließen von Ressourcen realisieren. C# nutzt statt try das spezielle Schlüsselwort using, mit Typen, die die Schnittstelle IDisposable implementieren, mit einer Methode Dispose() statt close(). (In Java sollte die Schnittstelle ursprünglich auch Disposable statt nun AutoCloseable heißen.) In Python 2.5 wurde ein context management protocol mit dem Schlüsselwort with realisiert, sodass Python automatisch bei Betreten eines Blockes __enter__() aufruft und beim Verlassen die Methode __exit__(). Das ist insofern interessant, als dass hier zwei Methoden zur Verfügung stehen. Bei Java ist es nur close() beim Verlassen des Blockes, aber es gibt keine Methode zum Betreten eines Blockes; so etwas muss beim Anlegen der Ressource erledigt werden.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java 7 – Mehr als eine Insel





 Java 7 –
 Mehr als eine Insel


Zum Katalog: Android 3






 Android 3


Zum Katalog: Android-Apps entwickeln






 Android-Apps
 entwickeln


Zum Katalog: NetBeans Platform 7






 NetBeans
 Platform 7


Zum Katalog: Einstieg in Eclipse 3.7






 Einstieg in
 Eclipse 3.7


Zum Katalog: Einstieg in Java






 Einstieg
 in Java


Zum Katalog: Einstieg in Java 7






 Einstieg in
 Java 7


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2011
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de