1 Dateiklasse
File
Der Name der (Datei-)Klasse ist etwas irreführend ; Wir denken vielleicht, dass es sich um eine Datei handelt, aber das ist nicht der Fall. Es kann nicht nur den Namen einer spezifischen Datei darstellen, sondern auch den einer Gruppe von Dateien in einem Verzeichnis. >Name. Tatsächlich istFilePath
(Dateipfad) ein besserer Name für diese Klasse.
Wenn es sich um eine Sammlung von Dateien handelt, können wir die Methode list()
für diese Sammlung aufrufen, die ein String-Array zurückgibt.
I/O-Klassenbibliotheken von Programmiersprachen verwenden häufig das abstrakte Konzept von Stream , das ein beliebiges Datenquellenobjekt darstellt, das in der Lage ist, Daten zu erzeugen oder ein Empfangsobjekt, das in der Lage ist, Daten zu empfangen. „Streams“ maskieren die Details der Datenverarbeitung im eigentlichen E/A-Gerät.
Wir verwenden selten eine einzelne Klasse, um ein Flussobjekt zu erstellen, sondern stellen stattdessen die gewünschte Funktionalität durch Überlagerung mehrerer Objekte bereit (dies ist das Dekorator-Entwurfsmuster). Tatsächlich ist der Hauptgrund, warum die „Stream“-Bibliothek in Java verwirrend ist, dass zum Erstellen eines einzelnen Ergebnisstreams mehrere Objekte erstellt werden müssen.
In Java 1.0 definierte der Designer der Klassenbibliothek zunächst, dass alle Klassen, die sich auf die -Eingabe beziehen, von InputStream
erben sollten, während 🎜>Ausgabe Alle Klassen, die sich auf beziehen, sollten von OutputStream
erben.
read() in InputStream oder Reader wird verwendet, um ein einzelnes Byte oder Byte-Array zu lesen, und OutputStream oder Writer wird verwendet, um ein einzelnes Byte oder Byte-Array zu schreiben .
InputStream
wird zur Darstellung von Klassen verwendet, die Eingabedaten aus verschiedenen Datenquellen generieren. Jede Datenquelle verfügt über eine entsprechende InputStream-Unterklasse . Zu diesen Datenquellen gehören:
Byte-Array. Ein Array von Bytes.
String-Objekte. Eine String-Objekt.
-Datei. Eine Datei.
„Eine Pipe“ (eine Pipe), funktioniert wie eine echte Pipe, d. h. die Eingabe erfolgt an einem Ende und die Ausgabe erfolgt am anderen Ende.
Eine Sequenz, die aus anderen Arten von Streams besteht, damit wir sie in einem einzigen Stream zusammenfassen können. Eine Folge anderer Streams.
Andere Datenquellen, wie Internetverbindungen usw. Andere Quellen.
Die Klasse OutputStream
bestimmt das Ziel, wohin die Ausgabe geht:
Wort Abschnittsarray
Datei
Pipeline
FilterInputStream
und FilterOutputStream
werden verwendet, um eine Decorator-Klassenschnittstelle bereitzustellen, um spezifische Eingabestream (InputStream) und Die beiden Klassen des Ausgabestreams (OutputStream), ihre Namen sind nicht intuitiv. Diese beiden Klassen sind für Dekorateure notwendig (um eine gemeinsame Schnittstelle für alle zu dekorierenden Objekte bereitzustellen).
und InputStream
OutputStream
stellen byteorientierte E/A in Form von Funktionen
Reader
Writer
sind zeichenorientierte I/O-Funktionen in Form von (Unicode-kompatibel)
undsind in erster Linie für die
5 Eigenständige Klasse: RandomAccessFileReader
Writer
Internationalisierung konzipiert. Die alte I/O-Stream-Vererbungshierarchie unterstützte nur Bit-Byte-Streams8
und verarbeitete Bit-Unicode-Zeichen nicht gut16
. Da Unicode für die Zeicheninternationalisierung verwendet wird (Javas eigenes Zeichen ist ebenfalls 16-Bit-Unicode), soll durch das Hinzufügen von und Vererbungshierarchie sichergestellt werden, dass alleReader
Writer
wird in allen E/A-Vorgängen unterstützt. Darüber hinaus ermöglicht das Design der neuen Klassenbibliothek einen schnelleren Betrieb als die alte Klassenbibliothek.Unicode
eignet sich für Dateien, die aus Datensätzen bekannter Größe bestehen, sodass wirzum Übertragen von Datensätzen verwenden können Bewegen Sie sich von einem Ort zum anderen und lesen oder ändern Sie dann Datensätze.
RandomAccessFile
RandomAccessFile
hat ein grundlegend anderes Verhalten als andere E/A-Typen, da wir uns innerhalb einer Datei vorwärts und rückwärts bewegen können. In jedem Fall ist es in sich geschlossen und wird direkt von Object abgeleitet.
Im Wesentlichen funktioniert RandomAccessFile wie die Kombination von DataInputStream und DataOutputStream, wobei einige neue Methoden hinzugefügt wurden:
getFilePointer()
Wird verwendet, um den aktuellen Dateispeicherort zu finden,
seek()
wird verwendet, um an einen neuen Speicherort innerhalb der Datei zu verschieben.
length()
wird verwendet, um die maximale Größe der Datei zu bestimmen.
Obwohl I/O-Streams auf unterschiedliche Weise in Kategorien kombiniert werden können, verwenden wir möglicherweise nur einige Kombinationen von ihnen. Die folgenden Beispiele können als grundlegende Referenz für die typische E/A-Nutzung verwendet werden.
Standard I Der Begriff /O bezieht sich auf das Unix-Konzept von „einem einzelnen Informationsfluss, der von einem Programm verwendet wird“.
Die Bedeutung von Standard-E/A besteht darin, dass wir Programme problemlos miteinander verbinden können und die Standardausgabe eines Programms zur Standardeingabe eines anderen Programms werden kann.
Die Geschwindigkeitssteigerung ergibt sich aus der Verwendung einer Struktur, die der Art und Weise, wie das Betriebssystem E/A durchführt, näher kommt: Kanäle und Puffer. Wir können es uns wie ein Kohlebergwerk vorstellen, die Kanäle sind die Lagerstätten mit den Kohleflözen (Daten) und die Puffer sind die Lastwagen, die zu den Lagerstätten geschickt werden. Die Lastwagen kommen mit Kohle beladen zurück und wir holen die Kohle von den Lastwagen. Mit anderen Worten: Wir interagieren nicht direkt mit dem Kanal, sondern lediglich mit dem Puffer und senden den Puffer an den Kanal. Ein Kanal erhält entweder Daten von einem Puffer oder sendet Daten an einen Puffer.
Die einzigen Puffer, die direkt mit Kanälen interagieren, sind
ByteBuffer
– also Puffer, die Rohbytes speichern können. Wenn wir die JDK-Dokumentation nachjava.nio.ByteBuffer
abfragen, stellen wir fest, dass es sich um eine recht einfache Klasse handelt: Sie erstellt einByteBuffer
-Objekt, indem sie ihm mitteilt, wie viel Speicher zugewiesen werden soll, und verfügt außerdem über eine Auswahl an Methoden, die die Rohbytes zurückgeben Daten aus formalen oder primitiven Datentypen ausgeben und lesen. Allerdings gibt es keine Möglichkeit, Objekte auszugeben oder zu lesen, nicht einmal String-Objekte. Dies ist ein Low-Level-Ansatz, aber er ist genau richtig, da er in den meisten Betriebssystemen die effizientere Methode zur Zuordnung darstellt.
FileChannel
ist manipuliert den Byte-Stream . Drei Klassen in der alten I/O-Klassenbibliothek wurden geändert, um FileChannel
zu generieren:
FileInputStream.getChannel()
FileOutputSteam. getChannel()
RandomAccessFile.getChannel()
package net.mrliuli.io.nio;import java.nio.*;import java.nio.channels.*;import java.io.*;public class GetChannel { private static final int BSIZE = 1024; public static void main(String[] args) throws Exception { // Write a file: FileChannel fc = new FileOutputStream("data.txt").getChannel(); fc.write(ByteBuffer.wrap("Some text ".getBytes())); /* ByteBuffer buffer = ByteBuffer.allocate(1024); fc.read(buffer); // NonReadableChannelException System.out.println((char)buffer.get()); */ fc.close(); // Add to the end of the file: fc = new RandomAccessFile("data.txt", "rw").getChannel(); // Readable and Writable fc.position(fc.size()); // Move to the end fc.write(ByteBuffer.wrap("some more".getBytes())); fc.close(); // Read the file: fc = new FileInputStream("data.txt").getChannel(); ByteBuffer buff = ByteBuffer.allocate(BSIZE); fc.read(buff); //fc.write(ByteBuffer.wrap("again".getBytes())); //NonWritableChannelException buff.flip(); while(buff.hasRemaining()) System.out.print((char)buff.get()); // ByteBuffer.get() returns a byte System.out.println(); } }
Bytes im Puffer speichern ByteBuffer-Methode:
Verwenden Sie put()
, um ein oder mehrere Bytes oder einen Wert eines Basisdatentyps direkt auszufüllen
, um das vorhandene Byte-Array in ByteBuffer einzuschließen. wrap()
:in
out
buffer
gibt die Bytes in in in.read(buffer);
ein. Zu diesem Zeitpunkt muss fc
Seien Sie darauf vorbereitet, dass andere Bytes von buffer
lesen. buffer.flip();
buffer
nach aus. Nach dem out.write(buffer)
-Vorgang befinden sich die Informationen noch im Puffer , muss buffer
out
write()
aufrufen, um alle internen Zeiger neu anzuordnen, sodass der Puffer bereit ist, Daten während eines anderen buffer
-Vorgangs zu akzeptieren.
package net.mrliuli.io.nio; import java.io.*; import java.nio.*; import java.nio.channels.*;public class ChannelCopy { private static final int BSIZE = 1024; public static void main(String[] args) throws Exception { if(args.length != 2){ System.out.println("arguments : sourcefile destfile"); System.exit(1); } // 打开一个FileChaanel用于读(输入) FileChannel in = new FileInputStream(args[0]).getChannel(); // 打开一个FileChannel用于写(输出) FileChannel out = new FileOutputStream(args[1]).getChannel(); // 一个缓冲器,分配了BSIZE个字节 ByteBuffer buffer = ByteBuffer.allocate(BSIZE); /* * return The number of bytes read, possibly zero, or <tt>-1</tt> if the channel has reached end-of-stream * FileChanel.read() * */ // -1 一个分界符(源于Unix和C),表示到达了输入的末尾 while(in.read(buffer) != -1){ buffer.flip(); // Prepare for writing out.write(buffer); // write()操作之后,信息仍在缓冲器中,clear()操作对所有的内部指针重新安排,以便缓冲器在另一个read()操作期间能够做好接受数据的准备。 buffer.clear(); // Prepare for reading } } }
缓冲器容纳的是普通的字节,为了把它们转换成字符,我们要么在输入它们的时候对其进行编码(这样,它们输出时才具有意义),要么在将其从缓冲器输出时对它们进行解码。可以使用
java.nio.charset.Charset
类实现这些功能,该类提供子把数据编码成多种不同类型的字符集的工具。The buffer contains plain bytes, and to turn these into characters, we must either encode them as we put them in (so that they will be meaningful when they come out) or decode them as they come out of the buffer. This can be accomplished using the java.nio.charset.Charset class, which provides tools for encoding into many different types of character set.
视图缓冲器(view buffer)可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。ByteBuffer依然是实际存储数据的地方,“支持”着前面的视图,因此对视图的任何修改都会映射成为对ByteBuffer中数据的修改。
文件加锁对其他的操作系统进程是可见的,因为Java的文件加锁直接映射到了本地操作系统的加锁工具。
exclusive lock 独占锁
Locking portions of a mapped file 对映射文件的部分加锁
cretical section 临界区
Java的对象序列化将那些实现了
Serilizable
接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可通过网络朝廷这意味着序列化机制能自动弥补不同操作系统之间的差异。就其本身来说,对象的序列化是非常有趣的,因为利用它可以实现轻量级持久性(ligthweight persistence)。持久性意味着一个对象的生存周期并不取决于程序是否正在执行,它可以生存于程序的调用之间。
对象序列化的概念加入到语言中是为了支持两种主要特性:
一是Java的远程方法调用(Remote Method Invocation, RMI),它使存活于其他计算机的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。
再者,对Java Beans来说,对象的序列化也是必需的。使用一个Bean时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动时进行后期恢复;这种具体工作就是由对象序列化完成的。
序列化一个对象和反序列化:
首先要创建一个ObjectOutputStream
对象,要通过构造函数含有一个 OutputStream
对象。
然后,只需调用 void writeObject(Object obj)
,即可将对象obj
序列化,即转换成字节序列输出到第一步所说的Outputstream
。
反序列化,即将字节序列还原为一个对象,则只需调用ObjectInputStream
的Object readObject()
,输入到一个InputStream
。
例:
Worm.java
反序列,即将字节序列还原为对象时,必须保证Java虚拟机能够找到要还原的对象的相关.class
文件,否则抛出java.lang.ClassNotFoundException
异常。
如果只希望一个对象的某些信息序列化而某些信息不序列化,即进行序列化控制,可使用Externalizable
接口。
Externalizable
接口继承自Serializable
接口,有两个方法如下,这两个方法会在序列化和反序列化过程中被自动调用。
void writeExternal(ObjectOutput obj)
,在该方法内部只对所需部分进行显式序列化。
void readExternal(ObjectInput in)
Externalizable
只序列化writeExternal()
中的部分,而Serializable
自动地全部序列化。
Externalizable
在反序列化时(即调用readObject()
时),会首先调用所有普通的默认构造器,然后调用readExternal()
。
Serializable
在反序列化时,对象完全以它存储的二进制位为基础来构造,而不用调用构造器。
例:
Blips.javaBlip3.java
transient
(瞬时)关键字如果我们正操作的是一个Serializable对象,那么所有序列化操作都会自动进行。为了能够予以控制,可以用
transient
(瞬时)关键字逐个字段地关闭序列化,它的意思是“不用麻烦你保存或恢复数据——我会自己处理的”。由于
Externalizable
对象在默认情况下不保存任何字段,所以transient
关键字只能和Serializable
对象一起使用。
我们可以通过一个字节数组来使用对象序列化,从而实现对任何可Serializable对象的“深度复制”(deep copy)——深度复制意味着我们复制的是整个对象网,而不仅仅是基本对象及其引用。
一个对象被序列化在单一流中,就可以恢复出与我们写出时一样的对象网,并且没有任何意外重复复制出的对象。
一个对象被序列化在不同流中,再从不同流恢复时,得到的对象地址不同。
例:
MyWorld.java
对象序列化的一个重要限制是它只是Java的解决方案:只有Java程序才能反序列化这种对象。一种更具互操作性的解决方案是将数据转换为XML格式,这可以使其被各种各样的平台语言使用。
相关文章:
Das obige ist der detaillierte Inhalt vonJava-Programmiergedanken-Lernklasse (5) Kapitel 18 – Java-IO-System. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!