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 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Die Funktionsbibliothek
10 Threads und nebenläufige Programmierung
11 Raum und Zeit
12 Datenstrukturen und Algorithmen
13 Dateien und Datenströme
14 Die eXtensible Markup Language (XML)
15 Grafische Oberflächen mit Swing
16 Grafikprogrammierung
17 Netzwerkprogrammierung
18 Verteilte Programmierung mit RMI und Web-Services
19 JavaServer Pages und Servlets
20 Applets
21 Midlets und die Java ME
22 Datenbankmanagement mit JDBC
23 Reflection und Annotationen
24 Logging und Monitoring
25 Sicherheitskonzepte
26 Java Native Interface (JNI)
27 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Stichwort

Download:
- ZIP, ca. 12,5 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel

Java ist auch eine Insel
7., aktualisierte Auflage
geb., mit DVD (November 2007)
1.492 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1146-8
Pfeil 13 Dateien und Datenströme
Pfeil 13.1 Datei und Verzeichnis
Pfeil 13.1.1 Dateien und Verzeichnisse mit der Klasse File
Pfeil 13.1.2 Verzeichnis oder Datei? Existiert es?
Pfeil 13.1.3 Verzeichnis- und Dateieigenschaften/-attribute
Pfeil 13.1.4 Wurzelverzeichnis, Laufwerksnamen, Plattenspeicher
Pfeil 13.1.5 Umbenennen und Verzeichnisse anlegen
Pfeil 13.1.6 Verzeichnisse listen und Dateien filtern
Pfeil 13.1.7 Dateien berühren, neue Dateien anlegen, temporäre Dateien
Pfeil 13.1.8 Dateien und Verzeichnisse löschen
Pfeil 13.1.9 Verzeichnisse nach Dateien iterativ durchsuchen
Pfeil 13.1.10 URL- und URI-Objekte aus einem File-Objekt ableiten
Pfeil 13.1.11 Mit Locking Dateien sperren
Pfeil 13.1.12 Sicherheitsprüfung
Pfeil 13.1.13 Mime-Typen mit dem JavaBeans Activation Framework (JAF)
Pfeil 13.1.14 Zugriff auf SMB-Server mit jCIFS
Pfeil 13.2 Dateien mit wahlfreiem Zugriff
Pfeil 13.2.1 Ein RandomAccessFile zum Lesen und Schreiben öffnen
Pfeil 13.2.2 Aus dem RandomAccessFile lesen
Pfeil 13.2.3 Schreiben mit RandomAccessFile
Pfeil 13.2.4 Die Länge des RandomAccessFile
Pfeil 13.2.5 Hin und her in der Datei
Pfeil 13.2.6 Wahlfreier Zugriff und Pufferung mit Unified I/O
Pfeil 13.3 Stream-Klassen und Reader/Writer am Beispiel von Dateien
Pfeil 13.3.1 Mit dem FileWriter Texte in Dateien schreiben
Pfeil 13.3.2 Zeichen mit der Klasse FileReader lesen
Pfeil 13.3.3 Kopieren mit FileOutputStream und FileInputStream
Pfeil 13.3.4 Das FileDescriptor-Objekt
Pfeil 13.4 Basisklassen für die Ein-/Ausgabe
Pfeil 13.4.1 Die abstrakten Basisklassen
Pfeil 13.4.2 Übersicht über Ein-/Ausgabeklassen
Pfeil 13.4.3 Die abstrakte Basisklasse OutputStream
Pfeil 13.4.4 Die Schnittstellen Closeable und Flushable
Pfeil 13.4.5 Ein Datenschlucker
Pfeil 13.4.6 Die abstrakte Basisklasse InputStream
Pfeil 13.4.7 Ressourcen wie Grafiken aus dem Klassenpfad und aus Jar–Archiven laden
Pfeil 13.4.8 Ströme mit SequenceInputStream zusammensetzen
Pfeil 13.4.9 Die abstrakte Basisklasse Writer
Pfeil 13.4.10 Die Schnittstelle Appendable
Pfeil 13.4.11 Die abstrakte Basisklasse Reader
Pfeil 13.5 Formatierte Textausgaben
Pfeil 13.5.1 Die Klassen PrintWriter und PrintStream
Pfeil 13.5.2 System.out, System.err und System.in
Pfeil 13.5.3 Geschützte Passwort-Eingaben mit der Klasse Console
Pfeil 13.6 Schreiben und Lesen aus Strings und Byte-Feldern
Pfeil 13.6.1 Mit dem StringWriter ein String-Objekt füllen
Pfeil 13.6.2 CharArrayWriter
Pfeil 13.6.3 StringReader und CharArrayReader
Pfeil 13.6.4 Mit ByteArrayOutputStream in ein Byte-Feld schreiben
Pfeil 13.6.5 Mit ByteArrayInputStream aus einem Byte-Feld lesen
Pfeil 13.7 Datenströme filtern und verketten
Pfeil 13.7.1 Streams als Filter verketten
Pfeil 13.7.2 Gepufferte Ausgaben mit BufferedWriter und BufferedOutputStream
Pfeil 13.7.3 Gepufferte Eingaben mit BufferedReader und BufferedInputStream
Pfeil 13.7.4 LineNumberReader zählt automatisch Zeilen mit
Pfeil 13.7.5 Daten mit der Klasse PushbackReader zurücklegen
Pfeil 13.7.6 DataOutputStream/DataInputStream
Pfeil 13.7.7 Basisklassen für Filter
Pfeil 13.7.8 Die Basisklasse FilterWriter
Pfeil 13.7.9 Ein LowerCaseWriter
Pfeil 13.7.10 Eingaben mit der Klasse FilterReader filtern
Pfeil 13.8 Vermittler zwischen Byte-Streams und Unicode-Strömen
Pfeil 13.8.1 Datenkonvertierung durch den OutputStreamWriter
Pfeil 13.8.2 Automatische Konvertierungen mit dem InputStreamReader
Pfeil 13.9 Kommunikation zwischen Threads mit Pipes
Pfeil 13.9.1 PipedOutputStream und PipedInputStream
Pfeil 13.9.2 PipedWriter und PipedReader
Pfeil 13.10 Datenkompression
Pfeil 13.10.1 Java-Unterstützung beim Komprimieren und Zusammenpacken
Pfeil 13.10.2 Datenströme komprimieren
Pfeil 13.10.3 Zip-Archive
Pfeil 13.10.4 Jar-Archive
Pfeil 13.11 Prüfsummen
Pfeil 13.11.1 Die Schnittstelle Checksum
Pfeil 13.11.2 Die Klasse CRC32
Pfeil 13.11.3 Die Adler32-Klasse
Pfeil 13.12 Persistente Objekte und Serialisierung
Pfeil 13.12.1 Objekte mit der Standard-Serialisierung speichern und lesen
Pfeil 13.12.2 Zwei einfache Anwendungen der Serialisierung
Pfeil 13.12.3 Die Schnittstelle Serializable
Pfeil 13.12.4 Nicht serialisierbare Attribute aussparen
Pfeil 13.12.5 Das Abspeichern selbst in die Hand nehmen
Pfeil 13.12.6 Tiefe Objektkopien
Pfeil 13.12.7 Versionenverwaltung und die SUID
Pfeil 13.12.8 Wie die ArrayList serialisiert
Pfeil 13.12.9 Probleme mit der Serialisierung
Pfeil 13.12.10 Serialisieren in XML-Dateien
Pfeil 13.12.11 JavaBeans Persistence
Pfeil 13.12.12 XStream
Pfeil 13.13 Tokenizer
Pfeil 13.13.1 StreamTokenizer
Pfeil 13.13.2 CSV-(Comma Separated Values-)Dateien verarbeiten
Pfeil 13.14 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

13.7 Datenströme filtern und verketten Zur nächsten ÜberschriftZur vorigen Überschrift

So wie im alltäglichen Leben Filter beim Kaffee oder Fotoapparaten eine große Rolle spielen, so sind sie auch bei Datenströmen zu finden. Immer dann, wenn Daten von einer Quelle gelesen oder in eine Senke geschrieben werden, können Filter die Daten auf dem Weg verändern. Die Java-Bibliothek sieht eine ganze Reihe von Filtern vor, die sich zwischen die Kommunikation schalten können.


Eingabe Ausgabe Anwendung

BufferedInputStream

BufferedOutputStream

Daten puffern

BufferedReader

BufferedWriter

CheckedInputStream

CheckedOutputStream

Checksumme berechnen

DataInputStream

DataOutputStream

Primitive Datentypen aus und in den Strom

DigestInputStream

DigestOutputStream

Digest (Checksumme) mitberechnen

InflaterInputStream

DeflaterOutputStream

Kompression von Daten

LineNumberInputStream

Mitzählen von Zeilen

LineNumberReader

PushbackInputStream

Daten in den Lesestrom zurückgelegt

PushbackReader

CipherInputStream

CipherOutputStream

Daten verschlüsseln und entschlüsseln


Der CipherOutputStream stammt als Einziger aus dem Paket javax.crypto, alle anderen aus java.io.


Galileo Computing - Zum Seitenanfang

13.7.1 Streams als Filter verketten Zur nächsten ÜberschriftZur vorigen Überschrift

Die Funktionalität der bisher vorgestellten Ein-/Ausgabe-Klassen reicht für den Alltag zwar aus, doch sind Ergänzungen gefordert, die die Fähigkeiten der Klassen erweitern. So zum Beispiel beim Puffern. Da die Programmlogik zur Pufferung mit Daten unabhängig von der Quelle ist, aus der die Daten stammen, findet sich die Pufferung in einer extra Klasse. Java implementiert hier ein bekanntes Muster, das sich Dekorator nennt. Zwei Zeilen sollen dieses Prinzip verdeutlichen, um gepufferte Daten in eine Datei zu schreiben:

Writer fw = new FileWriter( filename ); 
Writer bw = new BufferedWriter( fw );

Der Konstruktor von BufferedWriter nimmt einen beliebigen anderen Writer auf, denn der Pufferung ist es egal, ob die Daten in eine Datei oder ins Netzwerk geschrieben werden. Das Prinzip ist also immer, dass der Filter einen anderen Strom annimmt, an den er die Daten weitergibt, oder von dem er sie holt.

Schauen wir uns genau die Klassen im Paket java.io an, die andere Ströme im Konstruktor entgegennehmen:

  • BufferedWriter, PrintWriter, FilterWriter nimmt Writer
  • BufferedReader, FilterReader, LineNumberReader, PushbackReader, StreamTokenizer nimmt Reader
  • BufferedOutputStream, DataOutputStream, FilterOutputStream, ObjectOutputStream, OutputStreamWriter, PrintStream, PrintWriter nimmt OutputStream
  • BufferedInputStream, DataInputStream, FilterInputStream, InputStreamReader, ObjectInputStream, PushbackInputStream nimmt InputStream

Galileo Computing - Zum Seitenanfang

13.7.2 Gepufferte Ausgaben mit BufferedWriter und BufferedOutputStream Zur nächsten ÜberschriftZur vorigen Überschrift

Die Klassen BufferedWriter und BufferedOutputStream haben die Aufgabe, die mittels write() in den Ausgabestrom geleiteten Ausgaben zu puffern. Dies ist immer dann nützlich, wenn viele Schreiboperationen gemacht werden, denn das Puffern macht insbesondere Dateioperationen wesentlich schneller, da so mehrere Schreiboperationen zu einer zusammengefasst werden. Um die Funktionalität eines Puffers zu erhalten, besitzen die Klassen einen internen Puffer, in dem die Ausgaben von write() zwischengespeichert werden. Standardmäßig fasst der Puffer 8192 Symbole. Er kann aber über einen parametrisierten Konstruktor auf einen anderen Wert gesetzt werden. Erst wenn der Puffer voll ist oder die Methoden flush() oder close() aufgerufen werden, werden die gepufferten Ausgaben geschrieben. Durch die Verringerung tatsächlicher write()-Aufrufe an das externe Gerät erhöht sich die Geschwindigkeit der Anwendung im Allgemeinen deutlich.

Um einen BufferedWriter/BufferedOutputStream anzulegen, gibt es zwei Konstruktoren, denen ein bereits existierender Writer/OutputStream übergeben wird. An diesen Writer/OutputStream wird dann der Filter seinerseits die Ausgaben weiterleiten, insbesondere nach einem Aufruf von flush(), close() oder einem internen Überlauf.


class java.io.BufferedWriter     class java.io.BufferedOutputStream 
extends Writer                   extends FilterOutputStream

  • BufferedWriter( Writer out )
  • BufferedOutputStream( OutputStream out ) Erzeugt einen puffernden Ausgabestrom mit der Puffergröße von 8192 Symbolen.
  • BufferedWriter( Writer out, int sz )
  • BufferedOutputStream( OutputStream out, int size ) Erzeugt einen puffernden Ausgabestrom mit einer Puffergröße. Ist sie nicht echt größer 0, gibt es eine IllegalArgumentException.

Alle write(), append()-Funktionen sind so implementiert, dass die Daten erst im Puffer landen. Wenn der Puffer voll ist – oder flush() aufgerufen wird –, werden sie an den im Konstruktor übergebenen Writer durchgespült.

Beispiel zum BufferedWriter mit FileWriter und PrintWriter

Ein FileWriter sichert Daten in einer Datei. Ein BufferedWriter soll aber vorher die Daten erst einmal sammeln, sodass sie erst beim Flush an den FileWriter gehen. Der Anwendungsentwickler soll in unserem Beispiel aber nicht direkt den BufferedWriter nutzen, sondern ihn als allgemeinen Writer im Konstruktor von PrintWriter übergeben. Ein PrintWriter besitzt die komfortablen Funktionen print(), println() und printf(), sodass wir nicht mehr nur auf write()-Methoden vom Writer angewiesen sind.

Listing 13.26 com/tutego/insel/io/writer/ChainedWriter.java, main()

PrintWriter pw = null; 
try 
{ 
  Writer fw = new FileWriter( "charArrayWriterDemoPuffer.txt" ); 
  Writer bw = new BufferedWriter( fw ); 
  pw = new PrintWriter( bw ); 
 
  for ( int i = 1; i < 10000; i++ ) 
    pw.println( "Zeile " + i ); 
} 
catch ( IOException e ) { 
  System.err.println( "Error creating file!" ); 
} 
finally { 
  if ( pw != null ) 
    pw.close(); 
}

Zusätzlich bietet die Klasse BufferedWriter die Methode newLine(), die in der Ausgabe eine neue Zeile beginnt. Das Zeichen für den Zeilenwechsel wird aus der Systemeigenschaft line.separator genommen. Da sie intern mit der write()-Methode arbeitet, kann sie eine IOException auslösen.


Tipp Tipp Ein Geschwindigkeitstipp noch zum Schluss. Falls bekannt, sollte die Puffergröße des BufferedWriter (Gleiches gilt für BufferedReader) mit der internen Puffergröße des Betriebssystems übereinstimmen. Hier können Geschwindigkeitsmessungen mit unterschiedlichen Puffergrößen die Lösung bringen.



Galileo Computing - Zum Seitenanfang

13.7.3 Gepufferte Eingaben mit BufferedReader und BufferedInputStream Zur nächsten ÜberschriftZur vorigen Überschrift

Die Klassen BufferedReader und BufferedInputStream puffern Eingaben. Die Daten werden also zuerst in einen Zwischenspeicher geladen, was insbesondere bei Dateien zu weniger Zugriffen auf den Datenträger führt, was die Geschwindigkeit der Anwendung erhöht.

Die Klassen BufferedReader und BufferedInputStream besitzen je zwei Konstruktoren. Bei einem lässt sich die Größe des internen Puffers angeben. Die Puffergröße beträgt wie beim BufferedWriter/BufferedOutputStream standardmäßig 8192 Einträge.


class java.io.BufferedReader     class java.io.BufferedInputStream 
extends Reader                   extends FilterInputStream

  • BufferedReader( Reader in )
  • BufferedInputStream( InputStream in ) Erzeugt einen puffernden Zeichenstrom mit der Puffergröße von 8192.
  • BufferedReader( Reader in, int sz )
  • BufferedInputStream( InputStream in, int size ) Erzeugt einen puffernden Zeichenstrom mit der gewünschten Puffergröße.

Programm zur Anzeige von Dateien

Das folgende Programm implementiert ein einfaches »cat«-Kommando [Der kurze Name cat stammt von »catenate«, einem Synonym für »concatenate«. ] von Unix, um Dateiinhalte über die Standardausgabe auszugeben. Die Dateinamen werden auf der Kommandozeile übergeben.

Listing 13.27 com/tutego/insel/io/stream/cat.java

package com.tutego.insel.io.stream; 
 
import java.io.*; 
 
class cat 
{ 
  public static void main( String[] args ) 
  { 
    for ( String filename : args ) { 
      try { 
        InputStream in = new BufferedInputStream( new FileInputStream(filename) ); 
 
        try { 
          for ( int c; (c = in.read()) != –1 /* EOF */; ) 
            System.out.write( c ); 
        } 
        finally { 
          in.close(); 
        } 
      } 
      catch ( IOException e ) { 
        System.out.println( "cat: cannot process " + filename ); 
        System.exit( 1 ); 
      } 
    } // end for 
  } 
}

Die Dateiangaben nimmt das Programm über die Kommandozeile entgegen; etwa so:

$ java com.tutego.insel.io.stream.Cat a.txt b.txt

Zeilen lesen mit BufferedReader und readLine()

Die Klasse BufferedReader stellt die Methode readLine() zur Verfügung, die eine komplette Textzeile liest und als String an den Aufrufer zurückgibt; BufferedOutputStream als Byte-orientierte Klasse bietet die Methode nicht an.


class java.io.BufferedReader 
extends Reader

  • String readLine() Liest eine Zeile bis zum Zeilenende und gibt den String ohne die Endzeichen zurück. Die Rückgabe ist null, wenn der Stream am Ende ist.

Da ein BufferedReader Markierungen und Sprünge erlaubt, werden die entsprechenden Funktionen von Reader überschrieben.


Galileo Computing - Zum Seitenanfang

13.7.4 LineNumberReader zählt automatisch Zeilen mit Zur nächsten ÜberschriftZur vorigen Überschrift

Aus BufferedReader geht direkt die – bisher einzige – Unterklasse LineNumberReader hervor, die Zeilennummern zugänglich macht. Sie verfügt damit natürlich auch über readLine(). Mit getLineNumber() und setLineNumber() lässt sich aber zusätzlich auf die Zeilennummer zugreifen. Dass die Zeilennummer auch geschrieben werden kann, ist sicherlich ungewöhnlich, intern wird aber nur die Variable lineNumber geschrieben; der Datenzeiger wird nicht verändert. Bei jedem read() untersuchen die Funktionen, ob im Eingabestrom ein »\n«, »\r« oder eine Folge dieser beiden Zeichen vorkommt. Wenn dies der Fall ist, inkrementieren sie die Variable lineNumber. Zeilennummern beginnen bei null.


class java.io.LineNumberReader 
extends BufferedReader

  • LineNumberReader( Reader in ) Dekoriert einen gegebenen Reader.
  • LineNumberReader( Reader in, int sz ) Dekoriert einen gegebenen Reader mit gegebener Puffer-Größe.
  • int getLineNumber() Liefert die aktuelle Zeilennummer.
  • void setLineNumber( int lineNumber ) Setzt die aktuelle Zeilennummer.

Galileo Computing - Zum Seitenanfang

13.7.5 Daten mit der Klasse PushbackReader zurücklegen Zur nächsten ÜberschriftZur vorigen Überschrift

Die Klassen PushbackReader und PushbackInputStream können schon gelesene Eingaben wieder in den Strom zurücklegen. Das ist nützlich für so genannte vorausschauende Parser, die eine Wahl aufgrund des nächsten gelesenen Zeichens treffen. Mit den beiden Klassen kann dieses Vorschau-Zeichen wieder in den Eingabestrom gelegt werden, wenn der Parser den Weg doch nicht verfolgen möchte. Der nächste Lesezugriff liest dann nämlich dieses zurückgeschriebene Zeichen.

Die Filterklassen besitzen einen internen Puffer beliebiger Größe, in dem Symbole gespeichert werden, um sie später zurückholen zu können. Im Folgenden wollen wir uns nur mit dem PushbackReader beschäftigen; die Nutzung der Klasse PushbackInputStream ist ähnlich.


class java.io.PushbackReader 
extends FilterReader

  • PushbackReader( Reader in ) Erzeugt einen PushbackReader aus dem Reader in mit der Puffergröße 1.
  • PushbackReader( Reader in, int size ) Erzeugt einen PushbackReader aus dem Reader in mit der Puffergröße size.

Um ein Zeichen oder eine Zeichenfolge wieder in den Eingabestrom zu legen, wird die Methode unread() ausgeführt.

  • public void unread( int c ) throws IOException
  • public void unread( char[] cbuf, int off, int len ) throws IOException
  • public void unread( char[] cbuf ) throws IOException Legt ein Zeichen oder ein Feld von Zeichen zurück in den Zeichenstrom.

PushbackReader ist ein Eingabefilter und die einzige Klasse, die direkt aus FilterReader abgeleitet ist.

Zeilennummern entfernen mit einem PushbackReader

Das nächste Programm demonstriert die Möglichkeiten eines PushbackReaders. Die Implementierung wirkt möglicherweise etwas gezwungen, sie zeigt jedoch, wie unread() eingesetzt werden kann. Das Programm löst folgendes Problem: Wir haben eine Textdatei (im Programm einfach als String über einen StringReader zur Verfügung gestellt), in der Zeilennummern mit dem String verbunden sind.

134Erste Zeile 
234Zeile

Wir wollen nun die Zahlen vom Rest der Zeilen trennen. Dazu lesen wir so lange die Zahlen ein, bis ein Zeichen folgt, bei dem Character.isDigit() die Rückgabe false ergibt. Dann wissen wir, dass wir keine Ziffer mehr im Strom haben. Das Problem ist nun, dass schon ein Zeichen mehr gelesen wurde. In einem normalen Programm ohne die Option, das Zeichen zurücklegen zu können, würde das etwas ungemütlich. Dieses Zeichen müsste dann gesondert behandelt werden, da es das erste Zeichen der neuen Eingabe ist und nicht mehr zur Zahl gehört. Doch an Stelle dieser Sonderbehandlung legen wir es einfach wieder mit unread() in den Datenstrom, und dann kann der nachfolgende Programmcode einfach so weitermachen, als ob nichts gewesen wäre. Dies ist besonders dann von Vorteil, wenn noch Unterprogramme im Einsatz sind, die nach dem Lesen der Zahl eine weitere Funktion aufrufen, die noch einmal alles lesen will. Nach der herkömmlichen Methode muss das gelesene Zeichen dann mit an die Funktion übergeben werden.

Listing 13.28 com/tutego/insel/io/stream/PushbackReaderDemo.java, main()

String s = "134Erste Zeile\n234Zeile"; 
 
PushbackReader in = new PushbackReader( new StringReader(s) ); 
 
for ( int c; ; ) 
{ 
  try 
  { 
    int number = 0; 
 
    // Lies Zahl, bis nichts mehr geht 
 
    while ( Character.isDigit((char)(c = in.read())) ) 
      number = (number * 10) + c – '0'; 
 
    if ( c == –1 )   // Ende der Eingabe? => Ende der Schleife 
      break; 
 
    in.unread( c );        // Letztes Zeichen wieder rein 
 
    System.out.print( number + ":" ); 
 
    // Jetzt ist das Zeichen wieder drin 
 
    while ( (c = in.read()) != –1 ) 
    { 
      System.out.print( (char)c ); 
 
      if ( c == '\n' ) 
        break; 
    } 
    if ( c == –1 ) 
      break; 
  } 
  catch ( EOFException e ) 
  { 
    break; 
  } 
}

PushbackReader und das fehlende readLine()

Da PushbackReader nicht von BufferedReader abgeleitet ist und auch selbst keine Methode readLine() anbietet, müssen wir mit einer kleinen Schleife selbst Zeilen lesen. Im Bedarfsfall muss die Zeichenkombination »\r\n« gelesen werden. So wie die Methode von uns jetzt programmiert ist, ist sie auf Unix-Plattformen eingeschränkt, die nur ein einziges Ende-Zeichen einfügen. Doch warum nutzen wir nicht readLine()? Wer nun auf die Idee kommt, folgende Zeilen zu schreiben, um doch in den Genuss der Methode readLine() zu kommen, ist natürlich auf dem Holzweg:

StringReader   sr = new StringReader( s ); 
BufferedReader br = new BufferedReader ( sr ); 
PushbackReader in = new PushbackReader( br ); 
... 
br.readLine();      // Achtung, br!!

Wenn wir dem PushbackReader das Zeichen wiedergeben, dann arbeitet der BufferedReader genau eine Ebene darüber und bekommt vom Zurückgeben nichts mit. Daher ist es sehr gefährlich, die Verkettung zu umgehen. Im konkreten Fall wird das unread() nicht durchgeführt, und das erste Zeichen nach der Zahl fehlt.


Galileo Computing - Zum Seitenanfang

13.7.6 DataOutputStream/DataInputStream Zur nächsten ÜberschriftZur vorigen Überschrift

Während der OutputStream nur einzelne Bytes bzw. Byte-Felder schreibt und der InputStream aus einer Eingabe Bytes lesen kann, erweiterten die Klassen DataOutputStream und DataInputStream diese Schreib- und Lesefähigkeit um primitive Datentypen. Die Vorgaben bekommen sie aus DataOutput und DataInput, die wir schon bei RandomAccessFile sahen. Wichtige Methoden sind zum Beispiel writeChar(char), writeInt(int), writeUTF(char) oder readUnsignedByte(), readLong(), readFully(byte[]).


Galileo Computing - Zum Seitenanfang

13.7.7 Basisklassen für Filter Zur nächsten ÜberschriftZur vorigen Überschrift

Als Basisklassen für existierende Filter – und insbesondere für eigene Filter – sieht die Standard-Bibliothek die Klassen FilterInputStream und FilterOutputStream für die Binärseite und FilterReader und FilterWriter für die Zeichenseite vor.

Eine konkrete Filter-Klasse überschreibt nötige Methoden ihrer Basisklassen (also vom InputStream, OutputStream, Reader oder Writer) und ersetzt diese durch neue Methoden mit erweiterter Funktionalität. Die folgende Abbildung stellt die zentralen Filter vor:

Am UML-Diagramm fällt besonders auf, dass jeder Filter zum einen selbst ein Stream ist und zum anderen einen Stream verwaltet. Damit nimmt er Daten entgegen und leitet sie gleich weiter. Das ist ein bekanntes Design-Pattern und nennt sich Dekorator.


Galileo Computing - Zum Seitenanfang

13.7.8 Die Basisklasse FilterWriter Zur nächsten ÜberschriftZur vorigen Überschrift

Die Basis für eigene zeichenorientierte Filter, die vor dem Verarbeiten vom Client modifiziert werden sollen, ist die abstrakte Klasse FilterWriter. Wir übergeben im Konstruktor ein Writer-Objekt, an das die späteren Ausgaben weitergeleitet werden. Das Konstruktor-Argument wird in dem protected-Attribut out des FilterWriter-Objekts gesichert. In der Unterklasse greifen wir darauf zurück, denn dort schickt der Filter seine Ausgaben hin.

Die Standardimplementierung der Klasse FilterWriter überschreibt drei der write()-Methoden so, dass die Ausgaben an den im Konstruktor übergebenen Writer gehen.


abstract class java.io.FilterWriter 
extends Writer

  • protected Writer out Der Ausgabestrom, an den die Daten geschickt werden. Im Konstruktor gesetzt.
  • protected FilterWriter( Writer out ) Erzeugt einen neuen filternden Writer.
  • void write( int c ) Schreibt ein einzelnes Zeichen.
  • void write( char[] cbuf, int off, int len ) Schreibt einen Teil eines Zeichenfelds.
  • void write( String str, int off, int len ) Schreibt einen Teil eines Strings.
  • void close() Schließt den Stream.
  • void flush() Leert den internen Puffer des Streams.

Die Klasse ist abstrakt, also können keine direkten Objekte erzeugt werden. Dennoch gibt es einen protected-Konstruktor, der für Unterklassen wichtig ist. Abgeleitete Klassen bieten in der Regel selbst einen Konstruktor mit dem Parameter vom Typ Writer an und rufen im Rumpf mit super(write) den geschützten Konstruktor der Oberklasse FilterWriter auf. Über die initialisierte geschützte Objektvariable out kommen wir dann an diesen Ur-Writer.


Galileo Computing - Zum Seitenanfang

13.7.9 Ein LowerCaseWriter Zur nächsten ÜberschriftZur vorigen Überschrift

Wir wollen im Folgenden einen Filter schreiben, der alle in den Strom geschriebenen Zeichen in Kleinbuchstaben umwandelt. Drei Dinge sind für einen eigenen FilterWriter nötig:

  • Die Klasse leitet sich von FilterWriter ab.
  • Unser Konstruktor nimmt als Parameter ein Writer-Objekt und ruft mit super(out) den Konstruktor der Oberklasse, also FilterWriter, auf. Die Oberklasse speichert das übergebene Argument in der geschützten Objektvariable out, sodass die Unterklassen darauf zugreifen können.
  • Wir überlagern die drei write()-Methoden und eventuell noch close() und flush(). Unsere write()-Methoden führen dann die Filterfunktionen aus und geben die wahren Daten an den Writer weiter.

Listing 13.29 com/tutego/insel/io/stream/LowerCaseWriterDemo.java

package com.tutego.insel.io.stream; 
 
import java.io.*; 
 
class LowerCaseWriter extends FilterWriter 
{ 
  public LowerCaseWriter( Writer writer ) 
  { 
    super( writer ); 
  } 
 
  @Override 
  public void write( int c ) throws IOException 
  { 
    out.write( Character.toLowerCase((char)c) ); 
  } 
 
  @Override 
  public void write( char[] cbuf, int off, int len ) throws IOException 
  { 
    out.write( String.valueOf(cbuf).toLowerCase(), off, len ); 
  } 
 
  @Override 
  public void write( String s, int off, int len ) throws IOException 
  { 
    out.write( s.toLowerCase(), off, len ); 
  } 
} 
 
public class LowerCaseWriterDemo 
{ 
  public static void main( String[] args ) 
  { 
    StringWriter sw = new StringWriter(); 
    PrintWriter  pw = new PrintWriter( new LowerCaseWriter( sw ) ); 
 
    pw.println( "Eine Zeile für klein und groß" ); 
 
    System.out.println( sw.toString() ); 
 } 
}

Galileo Computing - Zum Seitenanfang

13.7.10 Eingaben mit der Klasse FilterReader filtern topZur vorigen Überschrift

Wie das Schachteln von Ausgabeströmen, so ist auch das Verbinden mehrerer Eingabeströme möglich. Als abstrakte Basiszwischenklasse existiert hier FilterReader, die ein Reader-Objekt im Konstruktor übergeben bekommt. Dieser sichert das Argument in der protected-Variablen in (das gleiche Prinzip wie bei den anderen FilterXXX-Klassen). Der Konstruktor ist protected, da er von der Unterklasse mit super() aufgerufen werden soll. Standardmäßig leiten die Methoden vom FilterReader die Methoden an den Reader aus der Variablen in weiter; das heißt etwa: Wenn der FilterReader geschlossen wird, wird der Aufruf in.close() ausgeführt. Aus diesem Grunde muss der FilterReader auch alle Methoden von Reader überschreiben, da ja eine Umleitung stattfindet.


abstract class java.io.FilterReader 
extends Reader

  • protected Reader in Der Zeicheneingabestrom oder null, wenn der Strom geschlossen wurde.
  • protected FilterReader( Reader in ) Erzeugt einen neuen filternden Reader.

Die Methoden read(), read(char[] cbuf, int off, int len), skip(long n), ready(), markSupported(), mark(int readAheadLimit), reset() und close() werden überschrieben und leiten die Aufrufe direkt an Reader weiter. Lösen die Methoden eine Ausnahme aus, leitet der FilterReader sie standardmäßig an uns weiter.



Ihr Kommentar

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






<< zurück



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