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

Pfeil 19 Einführung in Dateien und Datenströme
Pfeil 19.1 Alte und neue Welt in java.io und java.nio
Pfeil 19.1.1 java.io-Paket mit File-Klasse
Pfeil 19.1.2 NIO.2 und das java.nio-Paket
Pfeil 19.1.3 java.io.File oder java.nio.*-Typen?
Pfeil 19.2 Dateisysteme und Pfade
Pfeil 19.2.1 FileSystem und Path
Pfeil 19.2.2 Die Utility-Klasse Files
Pfeil 19.3 Dateien mit wahlfreiem Zugriff
Pfeil 19.3.1 Ein RandomAccessFile zum Lesen und Schreiben öffnen
Pfeil 19.3.2 Aus dem RandomAccessFile lesen
Pfeil 19.3.3 Schreiben mit RandomAccessFile
Pfeil 19.3.4 Die Länge des RandomAccessFile
Pfeil 19.3.5 Hin und her in der Datei
Pfeil 19.4 Basisklassen für die Ein-/Ausgabe
Pfeil 19.4.1 Die vier abstrakten Basisklassen
Pfeil 19.4.2 Die abstrakte Basisklasse OutputStream
Pfeil 19.4.3 Die abstrakte Basisklasse InputStream
Pfeil 19.4.4 Die abstrakte Basisklasse Writer
Pfeil 19.4.5 Die Schnittstelle Appendable *
Pfeil 19.4.6 Die abstrakte Basisklasse Reader
Pfeil 19.4.7 Die Schnittstellen Closeable, AutoCloseable und Flushable
Pfeil 19.5 Lesen aus Dateien und Schreiben in Dateien
Pfeil 19.5.1 Byteorientierte Datenströme über Files beziehen
Pfeil 19.5.2 Zeichenorientierte Datenströme über Files beziehen
Pfeil 19.5.3 Die Funktion von OpenOption bei den Files.newXXX(…)-Methoden
Pfeil 19.5.4 Ressourcen aus dem Modulpfad und aus JAR-Dateien laden
Pfeil 19.6 Zum Weiterlesen
 

Zum Seitenanfang

19.4    Basisklassen für die Ein-/Ausgabe Zur vorigen ÜberschriftZur nächsten Überschrift

Unterschiedliche Klassen zum Lesen und Schreiben von Binär- und Zeichendaten sammeln sich im Paket java.io. Für die byteorientierte Verarbeitung, etwa von PDF- oder MP3-Dateien, gibt es andere Klassen als für Textdokumente, zum Beispiel HTML-Dokumente oder Konfigurationsdateien. Binär- von Zeichendaten zu trennen ist sinnvoll, da zum Beispiel beim Einlesen von Textdateien diese immer in Unicode konvertiert werden müssen, da Java intern alle Zeichen in Unicode kodiert.

Die Stromklassen aus dem java.io-Paket sind um drei zentrale Prinzipien aufgebaut:

  1. Es gibt abstrakte Basisklassen, die Operationen für die Ein-/Ausgabe vorschreiben.

  2. Die abstrakten Basisklassen gibt es einmal für Unicode-Zeichen und einmal für Bytes.

  3. Die Implementierungen der abstrakten Basisklassen realisieren entweder die konkrete Eingabe in eine bestimmte Ressource bzw. die Ausgabe aus einer bestimmten Ressource (etwa einer Datei oder einem Speicherbereich) oder sind Filter.

 

Zum Seitenanfang

19.4.1    Die vier abstrakten Basisklassen Zur vorigen ÜberschriftZur nächsten Überschrift

Da Klassen zum Lesen und Schreiben von Unicode-Zeichen und Bytes getrennt werden müssen, gibt es folglich Klassen zur Ein-/Ausgabe von Bytes (oder Byte-Arrays) und Klassen zur Ein-/Ausgabe von Unicode-Zeichen (Arrays oder Strings). Die Klassen lauten:

Basisklasse für

Bytes

(oder Byte-Arrays)

Zeichen

(oder Zeichen-Arrays)

Eingabe

InputStream

Reader

Ausgabe

OutputStream

Writer

Tabelle 19.2    Basisklassen für die Ein- und Ausgabe

Die vier Typen wollen wir Stromklassen nennen. In diesen Klassen sind die zu erwartenden Methoden wie read(…) und write(…) zu finden.

Die Klassen InputStream und OutputStream bilden die Basisklassen für alle byteorientierten Klassen und dienen somit als Bindeglied bei Methoden, die als Parameter ein Eingabe- und Ausgabe-Objekt verlangen. So ist ein InputStream nicht nur für Dateien denkbar, sondern auch für Daten, die über das Netzwerk kommen. Das Gleiche gilt für Reader und Writer; sie sind die abstrakten Basisklassen zum Lesen und Schreiben von Unicode-Zeichen und Unicode-Zeichenfolgen. Die Basisklassen geben abstrakte read(…)- oder write(…)-Methoden vor, und die Unterklassen überschreiben die Methoden, da nur sie wissen, wie etwas tatsächlich gelesen oder geschrieben wird.

 

Zum Seitenanfang

19.4.2    Die abstrakte Basisklasse OutputStream Zur vorigen ÜberschriftZur nächsten Überschrift

Wenn wir uns den OutputStream anschauen, dann sehen wir auf den ersten Blick, dass hier alle wesentlichen Operationen rund um das Schreiben versammelt sind. Der Clou bei allen Datenströmen ist, dass spezielle Unterklassen wissen, wie sie genau die vorgeschriebene Funktionalität implementieren. Das heißt, dass ein konkreter Datenstrom, der in Dateien oder in eine Netzwerkverbindung schreibt, weiß, wie Bytes in Dateien oder ins Netzwerk kommen. Und an der Stelle ist Java mit seiner Plattformunabhängigkeit am Ende, denn auf einer so tiefen Ebene können nur native Methoden die Bytes schreiben.

abstract class java.io.OutputStream

implements Closeable, Flushable
  • abstract void write(int b) throws IOException

    Schreibt ein einzelnes Byte in den Datenstrom.

  • void write(byte[] b) throws IOException

    Schreibt die Bytes aus dem Array in den Strom.

  • void write(byte[] b, int off, int len) throws IOException

    Schreibt Teile des Byte-Arrays, nämlich len Byte ab der Position off, in den Ausgabestrom.

  • void close() throws IOException

    Schließt den Datenstrom. Dies ist die einzige Methode aus Closeable.

  • void flush() throws IOException

    Schreibt noch im Puffer gehaltene Daten. Dies ist die einzige Methode aus der Schnittstelle Flushable.

  • static OutputStream nullOutputStream()

    Liefert einen OutputStream, der alle Bytes verwirft. Neu in Java 11.

Die IOException ist keine RuntimeException und muss behandelt werden.

[zB]  Beispiel

Die Klasse ByteArrayOutputStream ist eine Unterklasse von OutputStream und speichert alle Daten in einem internen byte-Array. Schreiben wir ein paar Daten mit den drei gegebenen Methoden hinein:

byte[] bytes = { 'O', 'N', 'A', 'L', 'D' };

// 0 1 2 3 4

ByteArrayOutputStream out = new ByteArrayOutputStream();

try {

out.write( 'D' ); // schreibe D

out.write( bytes ); // schreibe ONALD

out.write( bytes, 1, 2 ); // schreibe NA

System.out.println( out.toString( StandardCharsets.ISO_8859_1.name() ) );

}

catch ( IOException e ) {

e.printStackTrace();

}

Über konkrete und abstrakte Methoden *

Zwei Eigenschaften lassen sich an den Methoden vom OutputStream ablesen: zum einen, dass nur Bytes geschrieben werden, und zum anderen, dass nicht wirklich alle Methoden abstract sind und von Unterklassen für konkrete Ausgabeströme überschrieben werden müssen. Nur write(int) ist abstrakt und elementar. Das ist trickreich, denn tatsächlich lassen sich die Methoden, die ein Byte-Array schreiben, auf die Methode abbilden, die ein einzelnes Byte schreibt. Werfen wir einen Blick in den Quellcode der Bibliothek:

Listing 19.6    java/lang/OutputStream.java, Ausschnitt

public abstract void write(int b) throws IOException;



public void write(byte[] b) throws IOException {

write(b, 0, b.length);

}



public void write(byte b[], int off, int len) throws IOException {

if (b == null) {

throw new NullPointerException();

} else if ((off < 0) || (off > b.length) || (len < 0) ||

((off + len) > b.length) || ((off + len) < 0)) {

throw new IndexOutOfBoundsException();

} else if (len == 0) {

return;

}

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

write(b[off + i]);

}

}

An beiden Implementierungen ist zu erkennen, dass sie die Arbeit sehr bequem an andere Methoden verschieben. Doch diese Implementierung ist nicht optimal! Stellen wir uns vor, ein Dateiausgabestrom überschreibt nur die eine abstrakte Methode, die nötig ist. Und nehmen wir weiterhin an, dass unser Programm nun immer große Byte-Arrays schreibt, etwa eine 5-MiB-Datei, die im Speicher steht. Dann werden für jedes Byte im Byte-Array in einer Schleife alle Bytes der Reihe nach an eine vermutlich native Methode übergeben. Wenn es so implementiert wäre, könnten wir die Geschwindigkeit des Mediums überhaupt nicht nutzen, zumal jedes Dateisystem Funktionen bereitstellt, mit denen sich ganze Blöcke übertragen lassen. Glücklicherweise sieht die Implementierung nicht so aus, da wir in dem Modell vergessen haben, dass die Unterklasse zwar die abstrakte Methode implementieren muss, aber immer noch andere Methoden überschreiben kann. Ein Blick auf die Unterklasse FileOutputStream bestätigt dies.

[»]  Hinweis

Ruft eine Oberklasse eine abstrakte Methode auf, die später die Unterklasse implementiert, ist das ein Entwurfsmuster mit dem Namen Schablonenmuster oder auf Englisch template pattern.

Gleichzeitig stellt sich die Frage, wie ein OutputStream, der die Eigenschaften für alle erdenklichen Ausgabeströme vorschreibt, wissen kann, wie ein spezieller Ausgabestrom etwa mit close() geschlossen wird oder seine gepufferten Bytes mit flush() schreibt – die Methoden müssten doch auch abstrakt sein! Das weiß OutputStream natürlich nicht, aber die Entwickler haben sich dazu entschlossen, eine leere Implementierung anzugeben. Der Vorteil besteht darin, dass Unterklassen nicht verpflichtet werden, die Methoden immer sinnvoll zu überschreiben.

 

Zum Seitenanfang

19.4.3    Die abstrakte Basisklasse InputStream Zur vorigen ÜberschriftZur nächsten Überschrift

Das Gegenstück zu OutputStream ist InputStream; jeder binäre Eingabestrom wird durch die abstrakte Klasse InputStream repräsentiert. Die Konsoleneingabe System.in ist vom Typ InputStream. Die Klasse bietet mehrere readXXX(…)-Methoden und ist auch ein wenig komplexer als OutputStream.

abstract class java.io.InputStream

implements Closeable
  • int available() throws IOException

    Gibt die Anzahl der verfügbaren Zeichen im Datenstrom zurück, die sofort ohne Blockierung gelesen werden können.

  • abstract int read() throws IOException

    Liest ein Byte aus dem Datenstrom und liefert ihn zurück. Die Rückgabe ist –1, wenn der Datenstrom keine Daten mehr liefert. Falls Daten grundsätzlich noch verfügbar sind, blockiert die Methode. Der Rückgabetyp ist int, weil –1 (0xFFFFFFFF) das Ende des Datenstroms anzeigt und ein –1 als byte (das wäre 0xFF) nicht von einem normalen Datum unterschieden werden könnte. Schade, dass es für –1 in der Java-API keine Konstante gibt.[ 266 ](Es könnte TT_EOF aus der Klasse java.io.StreamTokenizer genutzt werden. Oracle selbst wollte keine Konstante spendieren und hat die Anfrage schnell geschlossen: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=1204354. )

  • int read(byte[] b) throws IOException

    Liest bis zu b.length Bytes aus dem Datenstrom und setzt sie in das Array b. Die tatsächliche Länge der gelesenen Bytes wird zurückgegeben und muss nicht b.length sein; es können auch weniger Bytes gelesen werden. In der Basisklasse InputStream ist das einfach als return read(b, 0, b.length); implementiert.

  • int read(byte[] b, int off, int len) throws IOException

    Liest den Datenstrom aus und setzt die Daten in das Byte-Array b, an der Stelle off beginnend. Zudem begrenzt len die maximale Anzahl der zu lesenden Bytes. Intern ruft die Methode zunächst read() auf, und wenn es zu einer Ausnahme kommt, endet damit auch unsere Methode mit einer Ausnahme. Es folgen wiederholte Aufrufe von read(), die dann enden, wenn read() die Rückgabe –1 liefert. Falls es zu einer Ausnahme kommt, wird diese aufgefangen und nicht gemeldet.

  • int readNBytes(byte[] b, int off, int len) throws IOException

    Versucht len viele Bytes aus dem Datenstrom zu lesen und in das Byte-Array zu setzen. Im Gegensatz zu read(byte[], int, int) übernimmt readNBytes(…) mehrere Anläufe, len viele Daten zu beziehen. Dabei greift es auf read(byte[], int, int) zurück.

  • byte[] readNBytes(int len) throws IOException

    Versucht len viele Bytes aus dem Strom zu lesen und in das Rückgabe-Array zu setzen. Das Array kann kleiner als len sein, wenn vorher das Ende des Strom erreicht wurde. Seit Java 9.

  • byte[] readAllBytes() throws IOException

    Liest alle verbleibenden Daten aus dem Datenstrom und liefert ein Array mit diesen Bytes als Rückgabe. Seit Java 9.

  • long transferTo(OutputStream out) throws IOException

    Liest alle Bytes aus dem Datenstrom aus und schreibt sie in out. Wenn es zu einer Ausnahme kommt, wird empfohlen, beide Datenströme zu schließen. Seit Java 9.

  • long skip(long n) throws IOException

    Überspringt eine Anzahl von Zeichen. Die Rückgabe gibt die tatsächlich gesprungenen Bytes zurück, was nicht mit n identisch sein muss.

  • boolean markSupported()

    Gibt einen Wahrheitswert zurück, der besagt, ob der Datenstrom das Merken und Zurücksetzen von Positionen gestattet. Diese Markierung ist ein Zeiger, der auf bestimmte Stellen in der Eingabedatei zeigen kann.

  • void mark(int readlimit)

    Merkt sich die aktuelle Position im Datenstrom. Ist markSupported() gleich true, können danach mindestes readlimit Bytes gelesen und kann wieder zu der gemerkten Stelle zurückgesprungen werden.

  • void reset() throws IOException

    Springt wieder zu der Position zurück, die mit mark() gesetzt wurde.

  • void close() throws IOException

    Schließt den Datenstrom. Operation aus der Schnittstelle Closeable.

  • static InputStream nullInputStream()

    Liefert einen InputStream, der keine Bytes liest.

Auffällig ist, dass bis auf mark(int) und markSupported() alle Methoden im Fehlerfall eine IOException auslösen. Bei der read(…)-Methode, die mehrere Bytes liest, wird immer ein Array als Puffer übergeben, in den der InputStream hineinschreibt; das gleiche Prinzip finden wir aber auch beim Reader. Es gibt keine Methode, die als Rückgabe ein Array mit den gelesenen Daten liefert. Das hat zwei Gründe: Zum einen werden Puffer in der Regel wiederverwendet und wir hätten so unter Umständen sehr viele Objekte, und zum anderen könnte der Datenstrom sehr groß oder sogar unendlich sein.

[»]  Hinweis

available() liefert die Anzahl der Bytes, die ohne Blockierung gelesen werden können. (»Blockieren« bedeutet, dass die Methode nicht sofort zurückkehrt, sondern erst wartet, bis neue Daten vorhanden sind.) Die Rückgabe von available() sagt nichts darüber aus, wie viele Zeichen der InputStream insgesamt hergibt. Während aber bei FileInputStream die Methode available() üblicherweise doch die Dateilänge liefert, ist dies bei den Netzwerk-Streams im Allgemeinen nicht der Fall.

 

Zum Seitenanfang

19.4.4    Die abstrakte Basisklasse Writer Zur vorigen ÜberschriftZur nächsten Überschrift

Die abstrakte Klasse Writer ist der Basistyp für alle zeichenbasierten schreibenden Klassen.

abstract class java.io.Writer

implements Appendable, Closeable, Flushable
  • void write(int c) throws IOException

    Schreibt ein einzelnes Unicode-Zeichen. Nur der niedrige Teil (16 Bit des int) der 32-Bit-Ganzzahl wird geschrieben.

  • void write(char[] cbuf) throws IOException

    Schreibt ein Array von Zeichen.

  • abstract void write(char[] cbuf, int off, int len) throws IOException

    Schreibt len Zeichen des Arrays cbuf ab der Position off.

  • void write(String str) throws IOException

    Schreibt den String str.

  • void write(String str, int off, int len) throws IOException

    Schreibt len Zeichen der Zeichenkette str ab der Position off.

  • Writer append(char c) throws IOException

    Hängt ein Zeichen an. Verhält sich wie write(c), nur liefert es, wie die Schnittstelle Appendable verlangt, ein Appendable zurück. Writer ist ein passendes Appendable.

  • Writer append(CharSequence csq) throws IOException

    Hängt eine Zeichenfolge an. Implementierung aus der Schnittstelle Appendable.

  • Writer append(CharSequence csq, int start, int end) throws IOException

    Hängt Teile einer Zeichenfolge an. Implementierung aus der Schnittstelle Appendable.

  • abstract void flush() throws IOException

    Schreibt den internen Puffer. Hängt verschiedene flush()-Aufrufe zu einer Kette zusammen, die sich aus der Abhängigkeit der Objekte ergibt. Die Methode schreibt alle Daten in den Puffer. Implementierung aus der Schnittstelle Flushable.

  • abstract void close() throws IOException

    Schreibt den gepufferten Strom und schließt ihn. Nach dem Schließen durchgeführte write(…)- oder flush()-Aufrufe bringen eine IOException mit sich. Ein zusätzliches close() löst keine Exception aus. Implementierung aus der Schnittstelle Closeable.

  • static Writer nullWriter()

    Ein Writer, der alle Zeichen verwirft. Die Rückgabe ist mit dem Unix-Device /dev/null vergleichbar. Neu in Java 11.

  • Eine übergebene null-Referenz führt immer zur Ausnahme.

 

Zum Seitenanfang

19.4.5    Die Schnittstelle Appendable * Zur vorigen ÜberschriftZur nächsten Überschrift

Alle Writer und auch die Klassen PrintStream, CharBuffer sowie StringBuffer und StringBuilder implementieren die Schnittstelle Appendable, die drei Methoden vorschreibt:

interface java.io.Appendable
  • Appendable append(char c)

    Hängt das Zeichen c an das aktuelle Appendable an und liefert das aktuelle Objekt vom Typ Appendable wieder zurück.

  • Appendable append(CharSequence csq)

    Hängt die Zeichenfolge an dieses Appendable an und liefert es wieder zurück.

  • Appendable append(CharSequence csq, int start, int end)

    Hängt einen Teil der Zeichenfolge an dieses Appendable an und liefert es wieder zurück.

Kovariante Rückgabe in Writer von Appendable

Die Klasse Writer demonstriert gut einen kovarianten Rückgabetyp, also dass der Rückgabetyp einer überschriebenen oder implementierten Methode ebenfalls ein Untertyp sein kann. So verfährt auch Writer, der die Schnittstelle Appendable implementiert. Die Methode append(…) in Writer besitzt nicht einfach den Rückgabetyp Appendable aus der Schnittstelle Appendable, sondern konkretisiert ihn zu Writer, das ein Untertyp von Appendable ist.

public Writer append( char c ) throws IOException {

write( c );

return this;

}
 

Zum Seitenanfang

19.4.6    Die abstrakte Basisklasse Reader Zur vorigen ÜberschriftZur nächsten Überschrift

Die abstrakte Klasse Reader dient dem Lesen von Zeichen aus einem zeichengebenden Eingabestrom. Die einzigen Methoden, die Unterklassen implementieren müssen, sind read (char[], int, int) und close(). Dies entspricht dem Vorgehen bei den Writer-Klassen, die auch nur write(char[], int, int) und close() implementieren müssen – und flush(), was es bei lesenden Strömen nicht gibt. Es bleiben demnach für die Reader-Klasse zwei abstrakte Methoden übrig. Die Unterklassen implementieren jedoch auch andere Methoden aus Geschwindigkeitsgründen neu.

abstract class java.io.Reader

implements Readable, Closeable
  • protected Reader()

    Erzeugt einen neuen Reader, der sich mit sich selbst synchronisiert.

  • protected Reader(Object lock)

    Erzeugt einen neuen Reader, der mit dem Objekt lock synchronisiert ist.

  • abstract int read(char[] cbuf, int off, int len) throws IOException

    Liest len Zeichen in den Puffer cbuf ab der Stelle off. Wenn len Zeichen nicht vorhanden sind, wartet der Reader. Die Methode gibt die Anzahl gelesener Zeichen zurück oder –1, wenn das Ende des Stroms erreicht wurde.

  • int read(CharBuffer target) throws IOException

    Liest Zeichen in den CharBuffer. Die Methode schreibt die Schnittstelle Readable vor.

  • int read() throws IOException

    Die parameterlose Methode liest das nächste Zeichen aus dem Eingabestrom. Sie wartet, wenn kein Zeichen im Strom bereitliegt. Der Rückgabewert ist ein int im Bereich von 0 bis 65.635 (0x0000–0xFFFF). Warum dann der Rückgabewert aber int und nicht char ist, kann leicht damit erklärt werden, dass die Methode den Rückgabewert –1 (0xFFFFFFFF) kodieren muss, falls der Datenstrom keine Daten mehr liefert.

  • int read(char[] cbuf) throws IOException

    Liest Zeichen aus dem Strom und schreibt sie in ein Array. Die Methode wartet, bis Eingaben anliegen. Der Rückgabewert ist die Anzahl der gelesenen Zeichen oder –1, wenn das Ende des Datenstroms erreicht wurde.

  • long transferTo(Writer out) throws IOException

    Liest alle Zeichen aus diesen Strom und schreibt sie nach out. Neu in Java 10.

  • abstract void close() throws IOException

    Schließt den Strom. Folgt anschließend noch ein Aufruf von read(…), mark(int) oder reset(), lösen die Methoden eine IOException aus. Ein doppelt geschlossener Stream hat keinen weiteren Effekt.

  • static Reader nullReader()

    Liefert einen Reader, der keine Zeichen liest. Neu in Java 11.

Get Ready

Zu diesen notwendigen Methoden, die bei der Klasse Reader gegeben sind, kommen noch weitere interessante Methoden hinzu, die den Status abfragen und Positionen setzen lassen. Die Methode ready() liefert als Rückgabe true, wenn ein read(…) ohne Blockierung der Eingabe möglich ist. Die Standardimplementierung der abstrakten Klasse Reader gibt immer false zurück.

[»]  Hinweis

Nehmen wir an, der Datenstrom soll komplett leergesaugt werden, bis keine Daten mehr verfügbar sind und der Datenstrom am Ende ist:

for ( int c; (c = reader.read()) != -1; )

System.out.println( (char) c );

Wir könnten auf die Idee kommen, die Lösung anders zu realisieren:

while ( reader.ready() )

System.out.println( (char) reader.read() );

Allerdings ist die Semantik eine ganz andere: Hier wird lediglich so lange gelesen, bis entweder der Datenstrom leer ist oder – und das ist der Punkt – es zum Blockieren kommt, wenn etwa über das Netzwerk keine Daten verfügbar sind und es etwas dauert, bis Nachschub kommt. Das heißt in diesem Fall: Wir bekommen nur die Daten bis zur Blockade, nicht aber alle Daten.

abstract class java.io.Reader

implements Readable, Closeable
  • boolean ready() throws IOException

    Liefert true, wenn aus dem Stream direkt gelesen werden kann. Das heißt allerdings nicht, dass false immer Blockieren bedeutet.

[»]  Hinweis

InputStream und Reader sind sich zwar sehr ähnlich, aber ein InputStream deklariert keine Methode ready(). Dafür gibt es in InputStream eine Methode available(), die sagt, wie viele Bytes ohne Blockierung gelesen werden können. Diese Methode gibt es wiederum nicht im Reader.

Sprünge und Markierungen

Mit der Methode mark(int) lässt sich die Position markieren, an der der Reader gerade steht. Ein Aufruf der Methode reset() setzt den Eingabestrom auf diese Position zurück. Das heißt, diese Stelle lässt sich zu einem späteren Zeitpunkt wieder anspringen. mark(int readAheadLimit) besitzt einen Ganzzahlparameter (int, nicht long), der angibt, wie viele Zeichen gelesen werden dürfen, bevor die Markierung nicht mehr gültig ist. Die Zahl ist wichtig, da sie die interne Größe des Puffers bezeichnet, der für den Strom angelegt werden muss.

Nicht jeder Datenstrom unterstützt diesen Rücksprung. Die Klasse StringReader unterstützt etwa die Markierung einer Position, die Klasse FileReader dagegen nicht. Daher sollte vorher mit markSupported() überprüft werden, ob das Markieren auch unterstützt wird. Wenn der Datenstrom es nicht unterstützt und wir diese Warnung ignorieren, werden wir eine IOException bekommen. Denn Reader implementiert mark(int) und reset() ganz einfach und muss von uns im Bedarfsfall überschrieben werden:

Listing 19.7    java/io/Reader.java, Ausschnitt

public void mark( int readAheadLimit ) throws IOException {

throw new IOException("mark() not supported");

}

public void reset() throws IOException {

throw new IOException("reset() not supported");

}

Daher gibt markSupported() auch in der Reader-Klasse false zurück.

abstract class java.io.Reader

implements Readable, Closeable
  • long skip(long n) throws IOException

    Überspringt n Zeichen. Blockt, bis Zeichen vorhanden sind. Gibt die Anzahl der wirklich übersprungenen Zeichen zurück.

  • boolean markSupported()

    Der Stream unterstützt die mark()-Operation.

  • void mark(int readAheadLimit) throws IOException

    Markiert eine Position im Stream. Der Parameter bestimmt, nach wie vielen Zeichen die Markierung ungültig wird, mit anderen Worten: Er gibt die Puffergröße an.

  • void reset() throws IOException

    Falls eine Markierung existiert, setzt der Stream an der Markierung an. Wurde die Position vorher nicht gesetzt, dann wird eine IOException mit dem String »Stream not marked« ausgelöst. Die API-Dokumentation lässt es mit der Bemerkung »könnte fehlschlagen« offen, wie die Methode reagieren soll, wenn in der Zwischenzeit mehr als readAheadLimit Zeichen gelesen wurden.

Reader implementiert die schon bekannte Schnittstelle Closeable mit der Methode close(). Und so, wie ein Writer die Schnittstelle Appendable implementiert, so implementiert ein Reader die Schnittstelle Readable und damit die Operation int read(CharBuffer target) throws IOException.

 

Zum Seitenanfang

19.4.7    Die Schnittstellen Closeable, AutoCloseable und Flushable Zur vorigen ÜberschriftZur nächsten Überschrift

Zwei besondere Schnittstellen, Closeable und Flushable, schreiben Methoden vor, die alle Ressourcen implementieren, die geschlossen werden und/oder Daten aus einem internen Puffer herausschreiben sollen.

Closeable

Alle lesenden und schreibenden Datenstromklassen, die geschlossen werden können, implementieren Closeable. In der Java SE sind das alle Reader/Writer- und InputStream/OutputStream-Klassen und weitere Klassen wie java.net.Socket.

interface java.io.Closeable

extends AutoClosable
  • void close() throws IOException

    Schließt den Datenstrom. Einen geschlossenen Strom noch einmal zu schließen, hat keine Konsequenz.

Die Schnittstelle Closeable erweitert java.lang.AutoCloseable (siehe Ablauf einer Ausnahmesituation), sodass alles, was Closeable implementiert, damit vom Typ AutoCloseable ist und als Variable bei einem try mit Ressourcen verwendet werden kann.

interface java.lang.AutoClosable
  • void close() throws Exception

    Schließt den Datenstrom. Einen geschlossenen Strom noch einmal zu schließen, hat keine Konsequenz.

Das Klassendiagramm zeigt die Vererbungsbeziehung zwischen »Closeable« und »AutoCloseable«.

Abbildung 19.4    Das Klassendiagramm zeigt die Vererbungsbeziehung zwischen »Closeable« und »AutoCloseable«.

[»]  Hinweis

Jeder InputStream, OutputStream, Reader und Writer implementiert close() – und mit dem close() auch den Zwang, eine geprüfte IOException zu behandeln. Bei einem Eingabestrom ist die Exception nahezu wertlos und kann auch tatsächlich ignoriert werden. Bei einem Ausgabestrom ist die Exception schon deutlich wertvoller. Das liegt an der Aufgabe von close(), die nicht nur darin besteht, die Ressource zu schließen, sondern vorher noch gepufferte Daten zu schreiben. Somit ist ein close() oft ein indirektes write(…), und hier ist es sehr wohl wichtig zu wissen, ob alle Restdaten korrekt geschrieben wurden. Die Ausnahme sollte auf keinen Fall ignoriert werden, und der catch-Block darf nicht einfach leer bleiben; Logging ist hier das Mindeste.

Flushable

Flushable findet sich nur bei schreibenden Klassen und ist insbesondere bei den Klassen wichtig, die Daten puffern:

interface java.io.Flushable
  • void flush() throws IOException

    Schreibt gepufferte Daten in den Strom.

Die Basisklassen Writer und OutputStream implementieren diese Schnittstelle, aber auch Formatter tut dies.

 


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