From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100127.html | 794 +++++++++++++++++++++ 1 file changed, 794 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100127.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100127.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100127.html b/Master/Reference Architectures and Patterns/hjp5/html/k100127.html new file mode 100644 index 0000000..31ec38e --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100127.html @@ -0,0 +1,794 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 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

+ +

+Basis der Ausgabe-Streams ist die abstrakte Klasse OutputStream. +Sie stellt folgende Methoden zur Verfügung: +

+ + + + + +
+ +
+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
+ +

+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. + + + + +

19.2.2 Aus OutputStream direkt abgeleitete Klassen

+ +

+Aus OutputStream +sind einige weitere Klassen direkt abgeleitet. Wie bei den Character-Streams +bestimmen sie im wesentlichen die Art bzw. das Ziel der Datenausgabe. + + + + +

FileOutputStream

+ +

+Der FileOutputStream +stellt einen Byte-Stream zur Ausgabe in eine Datei zur Verfügung. +Er besitzt folgende Konstruktoren: +

+ + + + + +
+ +
+public FileOutputStream(String name)
+  throws FileNotFoundException
+
+public FileOutputStream(String name, boolean append)
+  throws FileNotFoundException
+
+public FileOutputStream(File file)
+  throws IOException
+
+
+
+java.io.FileOutputStream
+ +

+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. + + +

+ + + + + +
+ +
+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
+ +Listing 19.1: Verwendung eines FileOutputStream

+ + + + +

ByteArrayOutputStream

+ +

+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. + + + + +

ObjectOutputStream

+ +

+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. + + + + +

PipedOutputStream

+ +

+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. + + + + +

19.2.3 Aus FilterOutputStream abgeleitete Klassen

+ +

+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

+ +

+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
+ + + + +

PrintStream

+ +

+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.

+ + + + +
 JDK1.1-6.0 
+
+ + + + +

DataOutput und DataOutputStream

+ +

+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. + +

+ + + + + + + + + + + + + + + + + + + + + + +
VonBisByteDarstellung
\u0000\u007F10nnnnnnn
\u0080\u07FF2110nnnnn 10nnnnnn
\u0800\uFFFF31110nnnn 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
+ +Listing 19.2: Verwendung der Klasse DataOutputStream

+ +

+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.

+ + + + +
 Tipp 
+
+ + + + +

Komprimieren von Dateien

+ +

+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
+ +Listing 19.3: Erstellen eines ZIP-Archivs

+

+ + + + + + + + + +
+ +

+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.

+ + + + +
 Hinweis 
+
+ + + + +

Berechnung von Prüfsummen

+ +

+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.

+ + + + +
 Hinweis 
+
+


+ + + +
 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 +
+ + + -- cgit v1.2.3