From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Basis der Ausgabe-Streams ist die abstrakte Klasse OutputStream.
+Sie stellt folgende Methoden zur Verfügung:
+
+
+Der parameterlose Konstruktor initialisiert einen OutputStream.
+Er ist protected
+und wird in abgeleiteten Klassen überlagert. Mit close
+wird der OutputStream
+geschlossen, und flush
+schreibt die gepufferten Daten physikalisch auf das Ausgabegerät
+und leert alle Puffer.
+
+
+Die write-Methoden
+erwarten Bytes oder Byte-Arrays als Daten. Wird ein byte-Array
+angegeben, so gibt die Klasse es vollständig aus, wenn nicht
+zusätzlich ein Arrayoffset und die Anzahl der zu schreibenden
+Bytes angegeben wird. Die Methode zum Schreiben eines einzelnen Bytes
+erwartet ein int
+als Argument, gibt aber lediglich seine unteren 8 Bit aus und ignoriert
+alle übrigen.
+
+
+
+
+
+Aus OutputStream
+sind einige weitere Klassen direkt abgeleitet. Wie bei den Character-Streams
+bestimmen sie im wesentlichen die Art bzw. das Ziel der Datenausgabe.
+
+
+
+
+
+Der FileOutputStream
+stellt einen Byte-Stream zur Ausgabe in eine Datei zur Verfügung.
+Er besitzt folgende Konstruktoren:
+
+
+Wird lediglich ein Dateiname angegeben, legt ein FileOutputStream
+die gewünschte Ausgabedatei neu an und setzt den Dateizeiger
+auf den Anfang der Datei. Wird der zweite Konstruktor verwendet und
+true
+als zweites Argument übergeben, wird der Dateizeiger auf das
+Ende der Datei positioniert, falls diese bereits existiert. Andernfalls
+entspricht das Verhalten dem des ersten Konstruktors. Der dritte Konstruktor
+entspricht dem ersten, erwartet aber ein File-Objekt
+anstelle eines Strings.
+
+
+Das folgende Programm zeigt beispielhaft die Anwendung eines FileOutputStream.
+Es legt die in der Kommandozeile angegebene Datei an (bzw. springt
+zu ihrem Ende, falls sie bereits vorhanden ist) und hängt 256
+Bytes mit den Werten 0 bis 255 an.
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 19 - Byte-Streams
+
+
+
+
+
+19.2 Ausgabe-Streams
+
+
+
+
+
+
+
+
+19.2.1 Die Basisklasse OutputStream
+
+
+
+
+
+
+
+
+
+
+protected OutputStream()
+
+public void close()
+public void flush()
+
+public void write(int b)
+public void write(byte[] b)
+public void write(byte[] b, int offs, int len)
+
+
+
+java.io.OutputStream
+19.2.2 Aus OutputStream direkt abgeleitete Klassen
+
+FileOutputStream
+
+
+
+
+
+
+
+
+
+
+public FileOutputStream(String name)
+ throws FileNotFoundException
+
+public FileOutputStream(String name, boolean append)
+ throws FileNotFoundException
+
+public FileOutputStream(File file)
+ throws IOException
+
+
+
+java.io.FileOutputStream
+
+
+
+Listing 19.1: Verwendung eines FileOutputStream
+
+
+
+
+
+001 /* Listing1901.java */
+002
+003 import java.io.*;
+004
+005 public class Listing1901
+006 {
+007 public static void main(String[] args)
+008 {
+009 try {
+010 FileOutputStream out = new FileOutputStream(
+011 args[0],
+012 true
+013 );
+014 for (int i = 0; i < 256; ++i) {
+015 out.write(i);
+016 }
+017 out.close();
+018 } catch (Exception e) {
+019 System.err.println(e.toString());
+020 System.exit(1);
+021 }
+022 }
+023 }
+
+
+Listing1901.java
+
+Die Klasse ByteArrayOutputStream +schreibt die auszugebenden Daten in ein byte-Array, +dessen Größe mit dem Datenvolumen wächst. Sie besitzt +zwei Konstruktoren: +
+
+
++public ByteArrayOutputStream() +public ByteArrayOutputStream(int size) ++ + |
++java.io.ByteArrayOutputStream | +
+Der parameterlose Konstruktor legt ein Ausgabearray mit einer anfänglichen +Größe von 32 Byte an, der andere erlaubt die freie Vorgabe +der initialen Puffergröße. + + + + +
+Ein ObjectOutputStream +erlaubt es, primitive Datentypen und komplette Objekte (inklusive +aller referenzierten Objekte) auszugeben. Zwar ist er nicht von FilterOutputStream +abgeleitet, wird aber ebenso verwendet und erwartet im Konstruktor +einen OutputStream +zur Weiterleitung der Ausgabedaten. Die Klasse ObjectOutputStream +ist eine der Säulen des Serialisierungs-APIs in Java. Sie wird +in Abschnitt 41.1.2 +ausführlich beschrieben. + + + + +
+Ein PipedOutputStream +dient zusammen mit einem PipedInputStream +zur Kommunikation zweier Threads. Beide zusammen implementieren eine +Message Queue, in die einer der beiden +Threads seine Daten hineinschreibt, während der andere sie daraus +liest. Ein ausführliches Beispiel zur Anwendung der beiden Klassen +findet sich in Abschnitt 22.4.4. + + + + +
+Die aus OutputStream +abgeleitete Klasse FilterOutputStream +ist die Basisklasse aller gefilterten Ausgabe-Streams. Diese definieren +kein eigenes Ausgabegerät, sondern bekommen es beim Instanzieren +in Form eines OutputStream-Arguments +übergeben: +
+
+
++public FilterOutputStream(OutputStream out) ++ + |
++java.io.FilterOutputStream | +
+Die Aufgabe der aus FilterOutputStream +abgeleiteten Klassen besteht darin, die Schreibzugriffe abzufangen, +in einer für sie charakteristischen Weise zu verarbeiten und +dann an das eigentliche Ausgabegerät (den im Konstruktor übergebenen +OutputStream) +weiterzuleiten. + + + + +
+BufferedOutputStream +puffert die Ausgabe in einen OutputStream. +Er kann insbesondere dann die Ausgabe beschleunigen, wenn viele einzelne +write-Aufrufe +erfolgen, die jeweils nur wenig Daten übergeben. Ein BufferedOutputStream +besitzt zwei zusätzliche Konstruktoren und eine Methode flush, +die dafür sorgt, dass die gepufferten Daten tatsächlich +geschrieben werden: +
+
+
++public BufferedOutputStream(OutputStream out) +public BufferedOutputStream(OutputStream out, int size) + +public void flush() + throws IOException ++ + |
++java.io.BufferedOutputStream | +
+Ein PrintStream +bietet die Möglichkeit, Strings und primitive Typen im Textformat +auszugeben. Er stellt eine Vielzahl von print- +und println-Methoden +für unterschiedliche Datentypen zur Verfügung. Seine Schnittstelle +und Anwendung entspricht der Klasse PrintWriter, +die in Abschnitt 18.2.3 +beschrieben wurde. +
+
![]() |
+![]() |
+
+
+ +Im Zuge der Internationalisierung des JDK wurden mit der Version 1.1 +die öffentlichen Konstruktoren der Klasse PrintStream +als deprecated +markiert (wegen der möglicherweise unzulänglichen Konvertierung +zwischen Bytes und Zeichen). Damit war die Klasse praktisch nicht +mehr verwendbar. Insbesondere war es nicht mehr möglich, die +Methoden setOut +und setErr +der Klasse System +sinnvoll zu verwenden (siehe Abschnitt 16.3.2). +Später wurde die Entscheidung als falsch angesehen und mit dem +JDK 1.2 revidiert. Seither sind die Konstruktoren wieder zulässig. +Einen einfach anzuwendenden Workaround, der die deprecated-Warnungen +im JDK 1.1 vermeidet, gibt es leider nicht. |
+
+
|
+![]() |
+
+Ein DataOutputStream +ermöglicht es, primitive Datentypen in definierter (und portabler) +Weise auszugeben. So geschriebene Daten können mit Hilfe eines +DataInputStream +wieder eingelesen werden. Ein DataOutputStream +implementiert das Interface DataOutput, +das folgende Methoden enthält: +
+
+
++void write(int b) + throws IOException +void write(byte[] b) + throws IOException +void write(byte[] b, int off, int len) + throws IOException +void writeBoolean(boolean v) + throws IOException +void writeByte(int v) + throws IOException +void writeShort(int v) + throws IOException +void writeChar(int v) + throws IOException +void writeInt(int v) + throws IOException +void writeLong(long v) + throws IOException +void writeFloat(float v) + throws IOException +void writeDouble(double v) + throws IOException +void writeBytes(String s) + throws IOException +void writeChars(String s) + throws IOException +void writeUTF(String str) + throws IOException ++ + |
++java.io.DataOutput | +
+Zu jeder einzelnen Methode ist in der JDK-Dokumentation genau angegeben, +auf welche Weise der jeweilige Datentyp ausgegeben wird. Dadurch ist +garantiert, dass eine mit DataOutputStream +geschriebene Datei auf jedem anderen Java-System mit einem DataInputStream +lesbar ist. Die Beschreibungen sind sogar so genau, dass Interoperabilität +mit Nicht-Java-Systemen erreicht werden kann, wenn diese in der Lage +sind, die primitiven Typen in der beschriebenen Weise zu verarbeiten. + +
+Eine Sonderstellung nimmt die Methode writeUTF +ein. Sie dient dazu, die 2 Byte langen UNICODE-Zeichen, mit denen +Java intern arbeitet, in definierter Weise in 1, 2 oder 3 Byte lange +Einzelzeichen zu verwandeln. Hat das Zeichen einen Wert zwischen \u0000 +und \u007F, wird es als Einzelbyte ausgeben. Hat es einen Wert zwischen +\u0080 und \u07FF, belegt es zwei Byte, und in allen anderen Fällen +werden drei Byte verwendet. Diese Darstellung wird als UTF-8-Codierung +bezeichnet und ist entsprechend Tabelle 19.1 +implementiert. Zusätzlich werden an den Anfang jedes UTF-8-Strings +zwei Längenbytes geschrieben. + +
+
| Von | +Bis | +Byte | +Darstellung |
| \u0000 | +\u007F | +1 | +0nnnnnnn |
| \u0080 | +\u07FF | +2 | +110nnnnn 10nnnnnn |
| \u0800 | +\uFFFF | +3 | +1110nnnn 10nnnnnn 10nnnnnn |
+Tabelle 19.1: Die UTF-8-Kodierung
+ ++Die UTF-8-Kodierung arbeitet bei den gebräuchlichsten Sprachen +platzsparend. Alle ASCII-Zeichen werden mit nur einem Byte codiert, +und viele andere wichtige Zeichen (insbesondere die im ISO-8859-Zeichensatz +definierten nationalen Sonderzeichen, aber auch griechische, hebräische +und kyrillische Zeichen), benötigen nur zwei Byte zur Darstellung. +Jedes Byte mit gesetztem High-Bit ist Teil einer Multibyte-Sequenz +und am ersten Byte einer Sequenz kann die Anzahl der Folgezeichen +abgelesen werden. + +
+Wir wollen uns ein Beispielprogramm ansehen: + + +
+
+
+
+001 /* Listing1902.java */
+002
+003 import java.io.*;
+004
+005 public class Listing1902
+006 {
+007 public static void main(String[] args)
+008 {
+009 try {
+010 DataOutputStream out = new DataOutputStream(
+011 new BufferedOutputStream(
+012 new FileOutputStream("test.txt")));
+013 out.writeInt(1);
+014 out.writeInt(-1);
+015 out.writeDouble(Math.PI);
+016 out.writeUTF("häßliches");
+017 out.writeUTF("Entlein");
+018 out.close();
+019 } catch (IOException e) {
+020 System.err.println(e.toString());
+021 }
+022 }
+023 }
+
+ |
++Listing1902.java | +
+Das Programm erzeugt eine Ausgabedatei test.txt
+von 38 Byte Länge (wie sie wieder eingelesen wird, zeigt Listing 19.5):
+
+
+00 00 00 01 FF FF FF FF-40 09 21 FB 54 44 2D 18 ........@.!.TD-.
+00 0B 68 C3 A4 C3 9F 6C-69 63 68 65 73 00 07 45 ..h....liches..E
+6E 74 6C 65 69 6E ntlein
+
+
+
+
![]() |
+![]() |
+
+
+ +DataOutput +wird nicht nur von DataOutputStream +implementiert, sondern auch von der Klasse RandomAccessFile, +die darüber hinaus das Interface DataInput +implementiert. Sollen primitive Daten wahlweise seriell oder wahlfrei +verarbeitet werden, ist es daher sinnvoll, die serielle Verarbeitung +mit Hilfe der Klassen DataOutputStream +und DataInputStream +vorzunehmen. Die Verarbeitung von Random-Access-Dateien wird in Kapitel 20 +behandelt. |
+
+
|
+![]() |
+
+Die aus FilterOutputStream +abgeleitete Klasse DeflaterOutputStream +ist die Basisklasse der beiden Klassen ZipOutputStream +und GZIPOutputStream +aus dem Paket java.util.zip. +Zudem ist ZipOutputStream +Basisklasse von JarOutputStream +aus dem Paket java.util.jar. +Sie alle dienen dazu, die auszugebenden Daten in eine Archivdatei +zu schreiben und platzsparend zu komprimieren: +
+Das folgende Listing zeigt, wie mehrere Dateien mit Hilfe eines ZipOutputStream +komprimiert und in eine gemeinsame Archivdatei geschrieben werden. +Es wird als Kommandozeilenprogramm aufgerufen und erwartet die Namen +der zu erstellenden Archivdatei und der Eingabedateien als Argumente. +Das erzeugte Archiv kann mit jar, +winzip +oder pkunzip +ausgepackt werden. + + +
+
+
+
+001 /* Zip.java */
+002
+003 import java.io.*;
+004 import java.util.zip.*;
+005
+006 public class Zip
+007 {
+008 public static void main(String[] args)
+009 {
+010 if (args.length < 2) {
+011 System.out.println("Usage: java Zip zipfile files...");
+012 System.exit(1);
+013 }
+014 try {
+015 byte[] buf = new byte[4096];
+016 ZipOutputStream out = new ZipOutputStream(
+017 new FileOutputStream(args[0]));
+018 for (int i = 1; i < args.length; ++i) {
+019 String fname = args[i];
+020 System.out.println("adding " + fname);
+021 FileInputStream in = new FileInputStream(fname);
+022 out.putNextEntry(new ZipEntry(fname));
+023 int len;
+024 while ((len = in.read(buf)) > 0) {
+025 out.write(buf, 0, len);
+026 }
+027 in.close();
+028 }
+029 out.close();
+030 } catch (IOException e) {
+031 System.err.println(e.toString());
+032 }
+033 }
+034 }
+
+ |
++Zip.java | +
+
![]() |
+
+
+ +Bitte beachten Sie, dass das Programm nur die Grundzüge des Erstellens +von ZIP-Dateien demonstriert. Insbesondere verzichtet es darauf, die +verschiedenen Eigenschaften des jeweiligen ZIP-Eintrags korrekt zu +setzen (Größe, Datum/Uhrzeit, Prüfsumme etc.). Um +dies zu tun, müssten die entsprechenden Daten ermittelt und dann +an die jeweiligen set-Methoden +des ZipEntry-Objekts +übergeben werden. |
+
+
|
+![]() |
+
+Soll sichergestellt werden, dass Daten während einer Übertragung +unverändert bleiben (etwa weil der Übertragungskanal unsicher +oder störanfällig ist), werden diese meist mit einer Prüfsumme +übertragen. Dazu wird aus den Originaldaten eine Art mathematische +Zusammenfassung gebildet und zusammen mit den Daten übertragen. +Der Empfänger berechnet aus den empfangenen Daten mit demselben +Verfahren die Prüfsumme und vergleicht sie mit der übertragenen. +Stimmen beide überein, kann davon ausgegangen werden, dass die +Daten nicht verfälscht wurden. + +
+Das Verfahren zur Berechnung der Prüfsumme muss natürlich +so beschaffen sein, dass möglichst viele Übertragungsfehler +aufgedeckt werden. Oder mit anderen Worten: es soll möglichst +unwahrscheinlich sein, dass zwei unterschiedliche Texte (von denen +der eine durch Modifikation des anderen enstanden ist) dieselbe Prüfsumme +ergeben. + +
+Im JDK können Prüfsummen beim Schreiben von Daten mit der +Klasse CheckedOutputStream +berechnet werden. Sie ist aus FilterOutputStream +abgeleitet und erweitert deren Schnittstelle um einen Konstruktor +zur Auswahl des Prüfsummenverfahrens und um die Methode getChecksum +zur Berechnung der Prüfsumme. Ihre Anwendung ist einfach und +entspricht der Klasse CheckedInputStream. +Ein Beispiel ist in Listing 19.6 +zu finden. +
+
![]() |
+
+
+ +Ein Message Digest ist eine erweiterte +Form einer Prüfsumme. Er besitzt zusätzliche Eigenschaften, +die ihn für kryptografische Anwendungen qualifizieren, und wird +in Abschnitt 48.1.3 erläutert. +Die Klasse DigestOutputStream +dient dazu, einen Message Digest für Daten zu berechnen, die +mit einem OutputStream +ausgegeben werden. Analog dazu gibt es eine Klasse DigestInputStream +zur Berechnung eines Message Digests für einen InputStream. |
+
+
|
+![]() |
+
| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |