Vorwort
Im vorherigen Artikel haben wir den relevanten Inhalt im Datei -Streaming -Framework von Java eingeführt, während sich unser Artikel auf den relevanten Inhalt des Dateizeichen -Streaming konzentriert.
Zunächst sollte klar sein, dass Byte -Stream -Verarbeitungsdateien auf Bytes basieren, während Charakter -Stream -Verarbeitungsdateien auf Zeichen als grundlegende Einheiten basieren.
Tatsächlich ist die Essenz des Zeichenstrombetriebs die Einkapselung der beiden Prozesse der "Byte -Stream -Operation" + "Codierung". Glaubst du das? Unabhängig davon, ob Sie ein Zeichen in eine Datei schreiben, müssen Sie die Zeichen in Binärdatum codieren und sie dann in Bytes als Grundeinheit in die Datei schreiben, oder Sie lesen ein Zeichen für den Speicher, Sie müssen es in Bytes als Basiseinheit lesen und dann in Zeichen transkodieren.
Es ist wichtig, dies zu verstehen, was Ihr Gesamtverständnis von Charakterströmen bestimmen wird. Schauen wir uns das Design verwandter APIs zusammen an.
Basisleser/Schriftsteller
Bevor wir die Charakter -Stream -Basisklasse formell lernen, müssen wir wissen, wie ein Charakter in Java dargestellt wird.
Zunächst ist die Standardcharaktercodierung in Java: UTF-8, und wir wissen, dass UTF-8-codierte Zeichen mit 1 bis 4 Bytes gespeichert werden und die häufiger verwendeten Zeichen, die weniger Bytes werden verwendet.
Der Zeichenentyp ist definiert als zwei Bytegrößen, dh für gewöhnliche Zeichen kann ein Zeichen einen Charakter speichern, aber für einige komplementäre Zeichensätze werden häufig zwei Zeichen verwendet, um einen Charakter darzustellen.
Der Leser ist die Basisklasse zum Lesen von Zeichenströmen und bietet die grundlegendsten Charakter -Lesevorgänge. Schauen wir uns zusammen.
Schauen wir uns zuerst seinen Konstruktor an:
Protected Object Lock; Protected Reader () {this.lock = this;} Protected Reader (Object Lock) {if (lock == null) {werfen neu nullPointerexception (); } this.lock = lock;}Der Leser ist eine abstrakte Klasse, daher besteht kein Zweifel daran, dass diese Konstruktoren zu Unterklassen aufgerufen werden und zur Initialisierung von Schleusenverriegelungsobjekten verwendet werden, die wir später ausführlich erklären werden.
public int read () löscht ioException {char cb [] = new char [1]; if (read (cb, 0, 1) == -1) return -1; sonst return cb [0];} public int read (char cbuf []) löst ioException {return read (cbuf, 0, cbuf.length);} Abstract public int read (char cbuf [], int off, int len) ausDie grundlegende Charakter -Leseoperation ist hier alles. Die erste Methode wird verwendet, um ein Zeichen zu lesen. Wenn es bis zum Ende der Datei gelesen wurde, gibt es -1 zurück. Gleiches gilt mit INT als Rückgabewerttyp. Warum nicht char verwenden? Der Grund ist derselbe, alles aufgrund der Unsicherheit der Interpretation des Wertes -1.
Die zweite Methode ähnelt der dritten Methode, wobei die Zeichen einer bestimmten Länge aus der Datei gelesen und in das Zielarray platziert werden. Die dritte Methode ist eine abstrakte Methode, die durch Unterklassen implementiert werden muss, während die zweite Methode darauf basiert.
Es gibt einige andere Methoden, die ähnlich sind:
Diese Methoden sind tatsächlich bekannt und ähneln im Allgemeinen unserem InputStream und haben keine Kernimplementierung. Ich werde hier nicht auf Details eingehen, Sie können grob wissen, was sich darin befindet.
Der Schriftsteller ist ein geschriebener Charakterstrom, der verwendet wird, um ein oder mehrere Zeichen in eine Datei zu schreiben. Natürlich ist die spezifische Schreibmethode immer noch eine abstrakte Methode und muss durch Unterklassen implementiert werden, sodass wir sie hier nicht wiederholen.
Adapter InpuststramReader/OutputStreamWriter
Adapter -Charakterströme erben vom Leser oder Schriftsteller der Basisklasse, die sehr wichtige Mitglieder des Charakterstream -Systems sind. Die Hauptfunktion besteht darin, einen Byte -Stream in einen Zeichenstrom umzuwandeln. Nehmen wir zuerst den Leseadapter als Beispiel.
Zunächst seine Kernmitglieder:
privates Final StreamDecoder SD;
StreamDecoder ist ein Decoder, mit dem verschiedene Operationen von Bytes in entsprechende Operationen von Zeichen umgewandelt werden. Wir werden es in der anschließenden Einführung kontinuierlich erwähnen, und wir werden es hier nicht einheitlich erklären.
Dann gibt es den Konstruktor:
public InputStreamReader (InputStream in) {Super (in); try {sd = streamDecoder.forinputStreamReader (in this, (String) null); } catch (unportedenCodingException e) {Neuen Fehler werfen (e); }} public InputStreamReader (InputStream in, String charSetName) löst eine nicht unterstützte AnscodingException {super (in) aus; if (charSetName == null) werfen neue nullpointerexception ("charSetName"); SD = StreamDeCoder.ForInputStreamReader (in this, charSetName);}Der Zweck dieser beiden Konstruktoren ist es, diesen Decoder zu initialisieren. Die Methode forInputStreamReader wird aufgerufen, die Parameter sind jedoch unterschiedlich. Schauen wir uns die Implementierung dieser Methode an:
Dies ist ein typisches statisches Fabrikmuster. Über die drei Parameter var0 und var1 gibt es nichts, was die Byte -Stream -Instanz bzw. die Adapterinstanz darstellt.
Der Parameter var2 repräsentiert tatsächlich einen Charakter -Codierungsnamen. Wenn es sich um NULL handelt, wird die System-Standardzeichenkodierung des Systems verwendet: UTF-8.
Schließlich können wir eine Instanz des Decoders bekommen.
Fast alle als nächstes eingeführten Methoden werden durch die Stütze auf diesen Decoder implementiert.
public String getEcoding () {return sd.getEcoding ();} public int read () löscht ioException {return sd.read ();} public int read (char cbuf [], int offset, int länge) {return Sd.Read (CBUF, Offset, Länge);Der Implementierungscode verwandter Methoden im Decoder ist noch relativ komplex. Wir werden hier keine eingehende Forschung durchführen, aber die allgemeine Implementierungsidee lautet: Der Prozess des "Byte-Stream-Lesens + Decoding".
Natürlich muss es eine entgegengesetzte Streamcoder -Instanz in OutputStreamwriter für Codierung von Zeichen geben.
Abgesehen davon sind der Rest der Operationen nicht anders, entweder in der Datei über ein Zeichenarray, über eine Zeichenfolge in die Datei geschrieben oder über die unteren 16 Bit von int in die Datei geschrieben.
File -Stream -Filereader/Schriftsteller
Der Zeichenstrom einer Datei kann als sehr einfach bezeichnet werden. Es gibt keine andere Methode außer dem Konstruktor, und es hängt vollständig vom Datei -Byte -Stream ab.
Nehmen wir Filereader als Beispiel.
FileReader erbt von InputStreamReader und hat nur die folgenden drei Konstruktoren: öffentlicher FileReader (String FileName) löscht FileNotFoundException {Super (neuer FileInputStream (DateiEname));} öffentliche Filere -Datei (Dateidatei) Throws FileNotfoundException (New FileInputScorne) (New FileInputream); FileInputStream (FD));}Theoretisch sollten alle Charakterströme auf unserem Adapter basieren, da nur sie eine Charakter-zu-Byte-Konvertierung bietet, unabhängig davon, ob Sie schreiben oder lesen, es ist unzertrennlich.
Unser Filerader erweitert keine eigenen Methoden. Die vorab implementierte Charakter-Betriebsmethode in der übergeordneten Klasse InputStreamReader reicht für ihn aus. Er muss nur in einer entsprechenden Byte -Stream -Instanz passieren.
Gleiches gilt für den Filewriter, ich werde hier nicht auf Details eingehen.
Charakter Array Stream ChararrayReader/Schriftsteller
Charakter -Arrays und Byte -Array -Streams sind ähnlich, sowohl für die Lösung der Situation, in der ungewisse Dateigröße besteht, und das Lesen einer großen Menge an Inhalten erfordert.
Da sie intern einen dynamischen Expansionsmechanismus liefern, können sie nicht nur die Zieldateien berücksichtigen, sondern auch die Array -Größe steuern, um nicht zu viel Speicher zuzuweisen und viel Speicherplatz zu verschwenden.
Nehmen Sie ChararrayReader als Beispiel
Protected char buf []; public ChararrayReader (char buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} public ChararrayReader (char buf [], int offset, int länge) {// ..}Die Kernaufgabe des Konstruktors besteht darin, ein Zeichenarray in das interne BUF -Attribut zu initialisieren. Alle nachfolgenden Lesevorgänge in der Zeichen -Array -Stream -Instanz basieren auf dem BUF -Zeichenarray.
In Bezug auf die anderen Methoden von ChararrayReader und ChararrayWriter werde ich sie hier nicht wiederholen, die im vorherigen Artikel im Grunde ähnlich dem Byte -Array -Stream ähneln.
Darüber hinaus sind auch ein StringReader und ein StringWriter beteiligt. Tatsächlich ist es im Wesentlichen das gleiche wie ein Charakter -Array -Stream. Immerhin ist die Essenz der String ein Zeichenarray.
BufferedReader/Writer
In ähnlicher Weise ist BufferedReader/Writer ein Pufferstrom und auch ein Dekorateurstrom, der zur Bereitstellung von Pufferfunktionen verwendet wird. Im Allgemeinen, ähnlich wie unser Byte -Puffer -Stream, stellen wir ihn hier kurz vor.
privater Leser in; privat char cb []; private static int defaultCharBufferSize = 8192; public bufferedReader (Leser in, int sz) {..} public bufferedReader (Leser in) {this (in, defaultCharBufferSize);};};};};CB ist ein Charakter -Array, das einige Zeichen zwischen dem Dateistrom zwischengespeichert. Sie können die Länge dieses Arrays im Konstruktor initialisieren, andernfalls wird der Standardwert von 8192 verwendet.
public int read () löscht ioException {..} public int read (char cbuf [], int off, int len) {...} ausIn Bezug auf das Lesen hängt es von der Lesemethode des Mitgliedsattributs in. Als Lesertyp hängt in der Lesemethode einer InputStream -Instanz, auf die sich intern stützt, häufig ab.
Daher können fast alle Zeichenströme nicht von einer Byte -Stream -Instanz getrennt werden.
Ich werde es hier über den BufferedWriter nicht wiederholen. Es ist im Grunde ähnlich, außer dass einer liest und der andere schreibt und es dreht sich um das interne Charakter -Array.
Standard -Ausdrucksstream
Es gibt zwei Haupttypen von Ausdruckströmen, Printstream und Printwriter. Ersteres ist ein Bytestrom und letzteres ist ein Charakterstrom.
In diesen beiden Streams wird in Betracht gezogen, um Streams in ihren jeweiligen Kategorien zu integrieren. Es gibt reichhaltige interne Einkapselungsmethoden, aber die Implementierung ist auch etwas kompliziert. Schauen wir uns zunächst den Printstream -Byte -Stream an:
Es gibt mehrere Hauptkonstruktoren:
Offensichtlich verlassen sich einfache Konstruktoren auf komplexe Konstruktoren, die bereits als "alte Routine" für JDK -Design gelten. Was es von anderen Byte -Streams unterscheidet, ist, dass PrintStream ein Flag -Autoflush bereitstellt, das angibt, ob der Cache automatisch aktualisiert werden soll.
Als nächstes kommt die Schreibmethode von Printstream:
Darüber hinaus fasst PrintStream auch eine große Anzahl von Druckmethoden zusammen und schreibt verschiedene Arten von Inhalten in Dateien, wie z. B.:
Natürlich schreiben diese Methoden nicht wirklich numerische Binärdatoren in eine Datei, sondern einfach ihre entsprechenden Zeichenfolgen in eine Datei, zum Beispiel:
Druck (123);
Die endgültige Datei ist nicht die Binäranweisung, die 123 entspricht, sondern nur die Zeichenfolge 123, die Druckstream.
Der von Printstream verwendete gepufferte Zeichenstrom implementiert alle Druckvorgänge. Wenn die automatische Aktualisierung angegeben wird, wird der Puffer automatisch aktualisiert, wenn Sie auf das neue Symbol "/n" stoßen.
PrintStream integriert alle Ausgabemethoden in Byte -Streams und Zeichenströme, wobei die Schreibmethode für Byte -Stream -Operationen verwendet wird und die Druckmethode für Zeichenstromvorgänge verwendet wird, die geklärt werden müssen.
Was den Printwriter betrifft, ist es ein vollwertiger Zeichenstrom, der vollständig gegen Zeichen arbeitet. Unabhängig davon, ob es sich um die Schreibmethode oder die Druckmethode handelt, handelt es sich um einen Zeichenstromvorgang.
Zusammenfassend haben wir drei Artikel ausgegeben, die Byte -Streams und Charakterstromoperationen in Java erklären. Byte -Streams vervollständigen die Datenübertragung zwischen Festplatte und Speicher basierend auf Bytes. Das typischste sind Dateizeichen -Streams, und ihre Implementierungen sind alle lokalen Methoden. Mit grundlegenden Byte -Transferfunktionen können wir auch die Effizienz durch Pufferung verbessern.
Die grundlegendste Implementierung von Zeichenströmen ist InputStreamReader und OutputStreamWriter. Theoretisch können sie bereits grundlegende Charakterstromvorgänge abschließen, sind jedoch nur auf die grundlegendsten Operationen beschränkt. Was für die Konstruktion ihrer Instanzen notwendig ist, ist "eine Byte -Stream -Instanz" + "Ein Codierungsformat".
Daher ist die Beziehung zwischen einem Charakterstrom und einem Byte -Stream genau wie die obige Gleichung. Der erforderliche Schritt zum Schreiben eines Zeichens in eine Festplattendatei besteht darin, das Zeichen im angegebenen Codierungsformat zu codieren und dann den Byte -Stream zu verwenden, um das codierte Zeichen binär in die Datei zu schreiben. Die Leseoperation ist das Gegenteil.
Alle Codes, Bilder und Dateien im Artikel werden in der Cloud auf meinem GitHub gespeichert:
(https://github.com/singleyam/overview_java)
Sie können auch lokal herunterladen.
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.