Das Konzept des „Streams“ geht auf das Konzept der Pipe in UNIX zurück. Unter UNIX ist eine Pipe ein ununterbrochener Bytestrom, der zur Implementierung der Kommunikation zwischen Programmen oder Prozessen oder zum Lesen und Schreiben von Peripheriegeräten, externen Dateien usw. verwendet wird. Sie schützt die Details der Datenverarbeitung im eigentlichen E/A-Gerät. Ein Stream muss eine Quelle und ein Ziel haben, bei denen es sich um bestimmte Bereiche des Computerspeichers, Festplattendateien oder sogar eine URL im Internet handeln kann. Die Flussrichtung ist wichtig. Je nach Flussrichtung kann der Fluss in zwei Kategorien unterteilt werden: Eingangsfluss und Ausgangsfluss. Tatsächlich dient die Eingabe/Ausgabe dem Speicher. Tatsächlich können die Quelle und das Ziel des Streams einfach als Produzenten und Konsumenten von Bytes betrachtet werden. Für den Eingabestream müssen Sie sich nicht um seine Quelle kümmern, solange Sie einfach die Daten aus dem Stream lesen für den Eingabestream Der Ausgabestream kann sein Ziel auch nicht kennen und einfach Daten in den Stream schreiben.
a. Stream: Eine Reihe geordneter Datensequenzen.
b Bytestrom: Die kleinste Dateneinheit im Datenstrom sind Bytes.
c. Zeichenstrom: Die kleinste Dateneinheit im Datenstrom ist ein Zeichen.
1. Die Klassen im java.io-Paket entsprechen zwei Arten von Streams
Ein Stream-Typ liest oder schreibt direkt von einem bestimmten Speicherort (z. B. einer Festplattendatei oder einem Speicherbereich). Dieser Stream-Typ wird als Knoten-Stream bezeichnet, andere Streams werden als Filter-Streams (Paket-Streams) bezeichnet.
Streams filtern: Einige Streams können Bytes aus Dateien und anderswo empfangen, und andere Streams können Bytes zu nützlicheren Datentypen kombinieren. Ein Konstruktor, der einen vorhandenen Stream an einen anderen Stream übergibt und die beiden Streams kombiniert. Der kombinierte Stream wird als gefilterter Stream bezeichnet. Der Filter-Eingabestream verwendet häufig andere Eingabestreams als Eingabequelle. Nach dem Filtern oder Verarbeiten wird er dem Benutzer in Form eines neuen Eingabestreams bereitgestellt. Wir verwenden selten eine einzelne Klasse, um ein Flussobjekt zu erstellen, sondern stellen stattdessen die gewünschte Funktionalität durch die Überlagerung mehrerer Objekte bereit (d. h. das Dekorator-Entwurfsmuster).
Die häufig verwendeten Eingabe- und Ausgabeströme von Java werden tatsächlich von vier abstrakten Klassen geerbt:
Basierend auf Single-Byte-InputStream, OutputStream-Klasse (byteorientierte E/A)
Reader- und Writer-Klassen basierend auf Doppelbyte-Unicode-Codeeinheiten (zeichenorientierte E/A)
Sobald der Eingabestream geöffnet ist, kann das Programm Daten seriell aus dem Eingabestream lesen. Der Prozess des Lesens/Schreibens von Daten aus dem Eingabestream läuft im Allgemeinen wie folgt ab: Öffnen eines Stream-Kanals -> Lesen/Schreiben von Informationen -> Schließen des Stream-Kanals.
Auf der Java-Plattform gibt es zwei Möglichkeiten, den Zeichenkodierungstyp der lokalen Plattform abzurufen:
(a) System.getProperty("file.encoding");
(b) Charset cs=Charset.defaultCharset();
Alle Eingabeströme und Ausgabeströme können in Byteströme (Eingabe, Ausgabe) und Zeichenströme (Eingabe, Ausgabe) unterteilt werden. Diejenigen, die Bytes verarbeiten, sind hauptsächlich (OutputStream/InputStream)-Serien, und diejenigen, die Zeichen verarbeiten, sind hauptsächlich (Reader/Write). ) Serie
2. Byteorientierte Eingabeströme (InputStream-Reihe). Diese Klassen können mit FileInputStream-Objekten verbunden werden, um nützliche Schnittstellen bereitzustellen:
ByteArrayInputStream: Verwenden Sie einen Puffer im Speicher als InputStream
StringBufferInputStream (in Java 1.1 veraltet): Verwenden Sie ein String-Objekt als InputStream, und die zugrunde liegende Implementierung verwendet StringBuffer
FileInputStream: Verwenden Sie eine Datei als InputStream, um den Dateilesevorgang zu implementieren (Dateiname, Datei, FileDescriptor-Objekt).
PipedInputStream: Implementiert das Pipe-Konzept, das hauptsächlich in Threads verwendet wird (als Datenquelle in mehreren Prozessen).
SequenceInputStream: Kombinieren Sie mehrere InputStreams zu einem InputStream
Byteorientierte Ausgabeströme (OutputStream-Serie) können mit FilterOutputStream-Objekten verbunden werden, um nützliche Schnittstellen bereitzustellen:
ByteArrayOutputStream: Erstellen Sie einen Puffer im Speicher, speichern Sie Informationen in einem Puffer im Speicher und initialisieren Sie die Puffergröße (optional).
FileOutputStream: Informationen in einer Datei speichern (Dateiname, Datei, FileDescriptor)
PipedOutputStream: implementiert das Pipe-Konzept, das hauptsächlich in Threads verwendet wird (gibt das Ziel der Daten für Multithreads an).
3. Die entsprechende (Reader/Writer)-Reihe :
Reader: Entspricht InputStream, Adapter InputStreamReader
Writer: entspricht OutputStream und der Adapter ist OutputStreamWriter
FileReader: Entspricht FileOutputStream
FileWriter: entspricht FileOurputStream
StringReader: Keine entsprechende Klasse
StringWriter: Entspricht ByteArrayInputStream
CharArrayReader: Entspricht ByteArrayOutputStream
CharArrayWriter: Entspricht ByteArrayOutputStream
PipedReader: Entspricht PipedInputStream
PipedWriter: Entspricht PipedOutputStream
4. Konvertierung zwischen zwei uneingeschränkten Streams ( unter Verwendung der Adapterklasse)
InputStreamReader und OutputStreamReader: Konvertieren Sie einen byteorientierten Stream in einen zeichenorientierten Stream.
InputStreamReader ist eine Brücke von einem Byte-Stream zu einem Zeichen-Stream: Er liest Bytes unter Verwendung eines angegebenen Zeichensatzes und dekodiert sie in Zeichen. Der verwendete Zeichensatz kann namentlich angegeben oder explizit angegeben werden oder den Standardzeichensatz der Plattform akzeptieren.
OutputStreamWriter ist eine Brücke vom Zeichenstrom zum Bytestrom: Zeichen, die in den Stream geschrieben werden sollen, können mithilfe des angegebenen Zeichensatzes in Bytes codiert werden. Der verwendete Zeichensatz kann namentlich angegeben oder explizit angegeben werden, andernfalls wird der Standardzeichensatz der Plattform akzeptiert
5. Lesen Sie Daten von InputStream über FilterInputStream :
DataInputStream: Basistypdaten (int, char, long usw.) aus dem Stream lesen.
BufferedInputStream: Verwenden Sie einen Puffer, um zu verhindern, dass jedes Mal ein tatsächlicher Lesevorgang durchgeführt werden muss
LineNumberInputStream: zeichnet die Anzahl der Zeilen im Eingabestream auf und ruft dann getLineNumber() und setLineNumber(int) auf.
PushbackInputStream: Wird selten verwendet und im Allgemeinen für die Compilerentwicklung verwendet
Schreiben Sie über FilterOutputStream in OutputStream:
DataIOutputStream: Daten vom Basistyp (int, char, long usw.) können entsprechend der Transplantationsmethode an den Stream ausgegeben werden.
BufferedOutputStream: Verwenden Sie einen Puffer, um das eigentliche Schreiben jedes Mal zu vermeiden, wenn Sie Daten senden
PrintStream: Erzeugt eine formatierte Ausgabe, wobei DataOutputStream die Datenspeicherung und PrintStream die Anzeige übernimmt
6. Ändern Sie das Verhalten des Streams
Obwohl BufferedOutputStream eine Unterklasse von FilterOutputStream ist, ist BufferedWriter keine Unterklasse von FilterWriter (FilterWriter ist eine abstrakte Klasse und hat keine Unterklassen).
Für DataInputStream gibt es keine entsprechende Klasse. Verwenden Sie DataInputStream, es sei denn, Sie verwenden stattdessen BufferedReader, wenn Sie readLine() verwenden möchten.
BufferedReader: entspricht BufferedInputStream
LineNumberReader: Entspricht LineNumberInputStream
PushBackReader: Entspricht PushbackInputStream
BufferedWrite: entspricht BufferedOutStream
PrintWrite: entspricht PrintStream
7. Selbstunabhängige Klasse: RandomAccessFile
Diese Klasse eignet sich für Dateien, die aus Datensätzen bekannter Größe bestehen. Zusätzlich zur Implementierung der Schnittstellen DataInput und DataOutput (DataInputStream und DataOutputStream implementieren diese beiden Schnittstellen) ist RandomAccessFile von Natur aus eine völlig unabhängige Klasse haben unterschiedliche Verhaltensweisen, können sich innerhalb einer Datei vorwärts und rückwärts bewegen und werden direkt von Object abgeleitet.
Lese- und Schreibvorgänge für Dateien können über das RandomAccessFile-Objekt ausgeführt werden.
Beim Generieren eines Objekts können Sie die Art der zu öffnenden Datei angeben: r, schreibgeschützt; w, schreibgeschützt;
Sie können direkt zur angegebenen Stelle in der Datei springen
Die meisten (aber nicht alle) Funktionen von RandomAccessFile wurden durch Nio-Speicherzuordnungsdateien ersetzt