ホームページ  >  記事  >  Java  >  Javaプログラミング思考学習教室(5) 第18章 - Java IOシステム

Javaプログラミング思考学習教室(5) 第18章 - Java IOシステム

php是最好的语言
php是最好的语言オリジナル
2018-08-09 14:45:091703ブラウズ

1 ファイルクラス

(File) クラスという名前はファイルを指すと思われるかもしれませんが、実際にはそうではありません。これは、特定のファイルのFile(文件)类这个名字有一定的误导性;我们可能会认为它指代的是文件,实际上却并非如此。它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。实际上,FilePath文件路径)对这个类来说是更好的名字。

  如果它指的是一个文件集,我们就可以对此集合调用list()方法,这个方法会返回一个字符串数组。

2 输入和输出(Input an output)

  编程语言的I/O类库常使用这个抽象概念,它代表任何有能力产出数据数据源对象或者是有能力接收数据接收对象。“流”屏蔽了实际的I/O设备中处理数据的细节。

我们很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所期望的功能(这是装饰器设计模式)。实际上,Java中的“流”类库让人迷惑的主要原因就在于:创建单一的结果流,却需要创建多个对象。

  在Java 1.0中,类库的设计者首先限定与输入有关的所有类都应该从InputStream继承,而与输出有关的所有类都应该从OutputStream继承。

  InputStream或Reader中的read()用于读取单个字节或者字节数组,OutputStream或Writer用于写单个字节或者字节数组。

2.1 InputStream类型

  InputStream的作用是用来表示那些从不同数据源产生输入数据的类。每一种数据源都有相应的InputStream子类。这些数据源包括:

  • 字节数组。An array of bbytes.

  • String对象。A String object.

  • 文件。A file.

  • “管道”(A pipe),工作方式与实际管道相似,即,从一端输入,从另一端输出。

  • 一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内。A sequence of other streams.

  • 其他数据源,如Internet连接等。Other sources.

2.2 OutputStream类型

  OutputStream类决定了输出所要去往的目标:

  • 字节数组

  • 文件

  • 管道

3 添加属性和有用接口

  FilterInputStreamFilterOutputStream 是用来提供装饰器类接口以控制特定输入流(InputStream)和输出流(OutputStream)的两个类,它们的名字并不直观。这两个类是装饰器的必要条件(以便能为所有正在被修饰的对象提供通用接口)。

4 Reader 和 Writer

  • InputStreamOutputStream面向字节形式的I/O提供功能

  • ReaderWriter面向字符(兼容Unicode)形式的I/O功能

设计ReaderWriter继承层次结构主要是为了国际化。老的I/O流继承层次结构仅支持8位字节流,并且不能很好地处理16位的Unicode字符。由于Unicode用于字符国际化(Java本身的char也是16位的Unicode),所以添加ReaderWriter继承层次结构就是为了在所有的I/O操作中都支持Unicode。另外新类库的设计使得它的操作比旧类库更快。

5 自我独立的类:RandomAccessFile

RandomAccessFile适用于由大小已知的记录组成的文件,所以我们可以使用seek()

🎜name🎜🎜だけでなく、ディレクトリ内の🎜ファイルセットの🎜🎜🎜name🎜🎜も表すことができます。実際、このクラスには 🎜FilePath🎜(🎜🎜ファイル パス🎜🎜) の方が適切な名前です。 🎜
🎜 ファイルのコレクションを参照している場合は、このコレクションに対して 🎜list()🎜 メソッドを呼び出すことができ、文字列配列が返されます。 🎜

2 入力と出力 (入力と出力)

🎜 プログラミング言語の I/O ライブラリでは、多くの場合、データを生成するあらゆる能力を表す🎜🎜ストリーム🎜🎜 の抽象的な概念が使用されます。 / em> の 🎜データ ソース オブジェクト🎜、またはデータを受信する機能を持つ 🎜受信オブジェクト🎜。 「ストリーム」は、実際の I/O デバイスでのデータ処理の詳細をマスクします。 🎜🎜🎜フロー オブジェクトの作成に単一のクラスを使用することはほとんどありませんが、複数のオブジェクトをオーバーレイすることで必要な機能を提供します (これがデコレーター デザイン パターンです)。実際、Java の「ストリーム」ライブラリがわかりにくい主な理由は、単一の結果ストリームを作成するために複数のオブジェクトを作成する必要があることです。 🎜🎜 Java 1.0 では、クラス ライブラリの設計者はまず、🎜input🎜 に関連するすべてのクラスは 🎜InputStream🎜 から継承し、🎜output🎜 に関連するすべてのクラスはすべて継承するように制限しました。 🎜OutputStream🎜 から継承されます。単一の🎜バイト🎜または🎜バイト配列🎜を読み取るには、InputStreamまたはReaderの🎜🎜 read()が使用され、単一のバイトまたはバイト配列を書き込むには、OutputStreamまたはWriterが使用されます。 🎜

2.1 InputStream 型

🎜 InputStream は、さまざまな 🎜データ ソース🎜 から入力データを生成するクラスを表すために使用されます。 🎜各データ ソースには、対応する InputStream サブクラスがあります🎜。これらのデータ ソースには、🎜
  • 🎜 バイト配列が含まれます。バイトの配列🎜
  • 🎜String オブジェクト。 String オブジェクト。🎜
  • 🎜 ファイル。ファイル。🎜
  • 🎜 「パイプ」(パイプ) は、実際のパイプと同様に機能します。つまり、入力は一方の端から行われ、出力はもう一方の端から行われます。 🎜
  • 🎜 他の種類のストリームを 1 つのストリームに収集できるように構成されたシーケンス。他のストリームのシーケンス。🎜
  • 🎜 インターネット接続などの他のデータ ソース。その他のソース。🎜

2.2 OutputStream タイプ

🎜 — OutputStream クラスは、出力の宛先を決定します: 🎜
  • 🎜バイト配列🎜
  • 🎜file🎜
  • 🎜pipe🎜

3 プロパティと便利なインターフェイスを追加します

🎜 FilterInputStreamFilterOutputStream は、🎜🎜固有の🎜🎜🎜入力ストリーム🎜 (InputStream) と🎜出力ストリーム🎜を制御するための🎜デコレータクラスインターフェース🎜を提供するために使用されます。 (OutputStream) 2 つのクラス、それらの名前は直感的ではありません。これら 2 つのクラスは、デコレーターに必要です (装飾されるすべてのオブジェクトに 🎜共通インターフェース🎜 を提供するため)。 🎜

4 リーダーおよびライター

  • 🎜InputStream および OutputStream🎜🎜バイト指向🎜🎜 形式の I/O は、関数 🎜
  • 🎜Reader および Writer を提供します🎜🎜文字指向の 🎜🎜 (Unicode 互換) 形式の I/O関数 🎜
🎜🎜 ReaderWriter の継承階層の設計は、主に 🎜🎜国際化🎜🎜 を目的としています。古い I/O ストリームの継承階層は、🎜8🎜 ビットのバイト ストリーム🎜🎜 のみをサポートし、🎜16🎜 ビットの Unicode 文字 🎜🎜 を適切に処理しません。 🎜🎜Unicode は文字の国際化 🎜🎜 に使用されるため (Java 自体の文字も 16 ビット Unicode)、ReaderWriter の継承階層を 🎜追加することで、すべての継承階層が保証されます。 Unicode🎜 はすべての I/O 操作でサポートされています。さらに、新しいクラス ライブラリの設計により、古いクラス ライブラリよりも動作が高速になります。 🎜

5 自己完結型クラス: RandomAccessFile

🎜🎜RandomAccessFile は既知のサイズのレコードで構成されるファイルに適しているため、seek() を使用できます。 レコードをある場所から別の場所に移動し、レコードを読み取るか変更します。 🎜

RandomAccessFile は、ファイル内で前後に移動できるため、他の I/O タイプとは根本的に動作が異なります。いずれの場合も、これは自己完結型であり、オブジェクトから直接派生されます。 RandomAccessFile拥有和别的I/O类型本质不同的行为,因为我们可以在一个文件 内向前和向后移动。在任何情况下,它都是自我独立的,直接从Object派生而来。

  从本质上说,RandomAccessFile的工作方式类似于把DataInputStream和DataOutputStream组合起来使用,还添加了一些新方法:

  • getFilePointer()用于查找当前所处的文件位置,

  • seek()用于在文件内移至新的位置,

  • length()用于判断文件的最大尺寸。

6 I/O流的典型使用方式(Typical uses of I/O streams)

尽管可以通过不同的方式组合I/O流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的I/O用法的基本参考。

6.1 缓冲输入文件(Buffered input file)

6.2 从内存输入(Input from memory)

6.3 格式化的内存输入(Formatted memory input)

6.4 基本的文件输出(Basic file output)

6.5 存储和恢复数据(Storing and recovering data)

6.6 读写随机访问文件(Reading and writing random-access files)

6.7 管道流(PipedStreams)

7 标准I/O(Standard I/O)

标准I/O这个术语参考的是Unix中“程序所使用的单一信息流”这个概念。

标准I/O的意义在于:我们可以很容易地把程序串联起来,一个程序的标准输出可以成为另一个程序的标准输入。

8 新I/O

速度的提高来自于所使用的结构更接近于操作系统执行I/O的方式:通道缓冲器。我们可以把它想像成一个煤矿,通道是一个包含煤层(数据)的矿藏,而缓冲器则是派送到矿藏的卡车。卡车载满煤炭而归,我们再从卡车上获得煤炭。也就是说,我们并没法有直接和通道交互,我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。

唯一直接与通道交互的缓冲器是ByteBuffer——也就是说,可以存储未加工字节的缓冲器。当我们查询JDK文档中的java.nio.ByteBuffer时,会发现它是相当基础的类:通过告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用于以原始的字节形式或基本数据类型输出和读取数据。但是,没办法输出或读取对象,即使是字符串对象也不行。这种处理虽然很低级,但却正好,因为这是大多数操作系统中更有效的映射方式。

8.1 通道FileChannel

  FileChannel操纵字节流的。旧I/O类库中有三个类被修改了,用以产生FileChannel:

  • 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();
    }
}

8.2 缓冲器ByteBuffer

  将字节存放于缓冲器ByteBuffer的方式:

  • 使用put()直接填充,填入一个或多个字节,或基本数据类型的值;

  • 使用wrap()将已存在的字节数组包装到ByteBuffer中。

8.3 read(), write(), flip(), write()

  设有一个输入通道in、一个输出通道out和一个缓冲器buffer:

  • in.read(buffer);fc中的字节输入buffer,此时必须再调buffer.flip();做好让别人从buffer读取字节的准备。

  • out.write(buffer)buffer中的字节输出到outwrite()操作之后,信息仍在缓冲器buffer中,须调用buffer.clear();对所有的内部指针重新安排,以便缓冲器在另一个read()

    基本的に、RandomAccessFile は DataInputStream と DataOutputStream を組み合わせるように動作しますが、いくつかの新しいメソッドが追加されています: 🎜
    • 🎜getFilePointer() は現在のファイルの場所を見つけるために使用されます。 🎜
    • 🎜seek() は、ファイル内の新しい場所に移動するために使用されます。 🎜
    • 🎜length() は、ファイルの最大サイズを決定するために使用されます。 🎜

    6 I/O ストリームの一般的な使用法

    🎜 I/O ストリーム クラスはさまざまな方法で組み合わせることができますが、使用できるのはそのうちのいくつかだけです。次の例は、一般的な I/O 使用法の基本的な参考資料として使用できます。 🎜

    6.1 バッファリングされた入力ファイル

    6.2 メモリからの入力

    6.3 フォーマットされたメモリ入力 )

    6.4 基本ファイル出力 (基本ファイル出力) )

    6.5 データの保存と回復 (データの保存と回復)

    6.6 ランダム アクセス ファイルの読み取りと書き込み (ランダム アクセス ファイルの読み取りと書き込み)

    6.7 PipedStreams

    7 標準 I/O

    🎜標準 I/Oこの用語は以下を指します。 Unix における「プログラムによって使用される単一の情報ストリーム」という概念。 🎜🎜 標準 I/O の重要性は、プログラムを簡単に接続でき、あるプログラムの標準出力が別のプログラムの標準入力になる可能性があることです。 🎜

    8 つの新しい I/O

    🎜 速度の向上は、オペレーティング システムが I/O を実行する方法に近い構造を使用することで実現します。チャネルバッファ。これを炭鉱のように考えることができます。チャネルは炭層 (データ) を含む鉱床であり、バッファは鉱​​床に派遣されるトラックです。トラックが石炭を積んで戻ってくるので、私たちはトラックから石炭を受け取ります。言い換えれば、チャネルと直接対話するのではなく、バッファと対話し、バッファをチャネルにディスパッチするだけです。チャネルは、バッファからデータを取得するか、バッファにデータを送信します。 🎜🎜 チャネルと直接やり取りする唯一のバッファーは ByteBuffer です。つまり、生のバイト バッファーを保存できます。 。 JDK ドキュメントで java.nio.ByteBuffer をクエリすると、これがかなり基本的なクラスであることがわかります。このクラスは、必要なストレージ領域の量を指示して ByteBuffer オブジェクトを作成します。また、生のバイト形式またはプリミティブ データ型でデータを出力および読み取るためのメソッドも選択できます。ただし、文字列オブジェクトであっても、オブジェクトを出力または読み取る方法はありません。この処理は低レベルですが、ほとんどのオペレーティング システムではマッピングのより効率的な方法であるため、これは適切です。 🎜

    8.1 チャネル FileChannel

    🎜 FileChannel は、バイト ストリームを操作します。 。古い I/O クラス ライブラリの 3 つのクラスは、FileChannel:🎜
    • 🎜FileInputStream.getChannel()🎜
    • 🎜FileOutputSteam.getChannel()🎜
    • 🎜RandomAccessFile.getChannel()🎜
    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
            }
    
        }
    
    }

    8.2 Buffer ByteBuffer

    🎜 格納方法バッファ ByteBuffer のセクション: 🎜
    • 🎜put()Fill を直接使用します。 、1 つ以上のバイト、または基本的なデータ型の値を入力します 🎜
    • 🎜wrap() ; >既存のバイト配列を ByteBuffer にパックします。 🎜

    8.3 read(), write(),flip(), write()

    🎜 入力チャンネル in と出力チャンネルがあります。 out とバッファ buffer:🎜
    • 🎜 in.read(buffer); fc のバイトを buffer に入力します。このとき、buffer.flip();他の人が buffer からバイトを読み取れるように準備してください。 🎜
    • 🎜out.write(buffer) buffer 内のバイトを out の場合、write() 操作の後、情報はまだバッファ buffer 内にあり、 は次のとおりです。 buffer.clear();と呼ばれます。すべての内部ポインタを再配置して、別の read() 操作中にバッファがデータを受け入れる準備ができるようにします。 🎜
    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
            }
    
        }
    
    }

    8.4 转换数据

    缓冲器容纳的是普通的字节,为了把它们转换成字符,我们要么在输入它们的时候对其进行编码(这样,它们输出时才具有意义),要么在将其从缓冲器输出时对它们进行解码。可以使用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.

    8.5 视图缓冲器

      视图缓冲器(view buffer)可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。ByteBuffer依然是实际存储数据的地方,“支持”着前面的视图,因此对视图的任何修改都会映射成为对ByteBuffer中数据的修改。

    8.6 文件加锁

      文件加锁对其他的操作系统进程是可见的,因为Java的文件加锁直接映射到了本地操作系统的加锁工具。

    exclusive lock 独占锁

    Locking portions of a mapped file 对映射文件的部分加锁

    cretical section 临界区

    9 压缩(Compression)

    10 对象序列化(Object serialization)

    Java的对象序列化将那些实现了Serilizable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可通过网络朝廷这意味着序列化机制能自动弥补不同操作系统之间的差异。

    就其本身来说,对象的序列化是非常有趣的,因为利用它可以实现轻量级持久性(ligthweight persistence)。持久性意味着一个对象的生存周期并不取决于程序是否正在执行,它可以生存于程序的调用之间。

      对象序列化的概念加入到语言中是为了支持两种主要特性:

    • 一是Java的远程方法调用(Remote Method Invocation, RMI),它使存活于其他计算机的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。

    • 再者,对Java Beans来说,对象的序列化也是必需的。使用一个Bean时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动时进行后期恢复;这种具体工作就是由对象序列化完成的。

      序列化一个对象和反序列化

    • 首先要创建一个ObjectOutputStream对象,要通过构造函数含有一个 OutputStream 对象。

    • 然后,只需调用 void writeObject(Object obj),即可将对象obj序列化,即转换成字节序列输出到第一步所说的Outputstream

    • 反序列化,即将字节序列还原为一个对象,则只需调用ObjectInputStreamObject readObject(),输入到一个InputStream

    例:

    Worm.java

    10.1 寻找类

      反序列,即将字节序列还原为对象时,必须保证Java虚拟机能够找到要还原的对象的相关.class文件,否则抛出java.lang.ClassNotFoundException异常。

    10.2 序列化的控制

      如果只希望一个对象的某些信息序列化而某些信息不序列化,即进行序列化控制,可使用Externalizable接口。

    10.2.1 Externalizable接口

      Externalizable接口继承自Serializable接口,有两个方法如下,这两个方法会在序列化和反序列化过程中被自动调用

    • void writeExternal(ObjectOutput obj),在该方法内部只对所需部分进行显式序列化

    • void readExternal(ObjectInput in)

    10.2.2 Externalizable接口与Serializable接口区别

    • Externalizable只序列化writeExternal()中的部分,而Serializable自动地全部序列化。

    • Externalizable在反序列化时(即调用readObject()时),会首先调用所有普通的默认构造器,然后调用readExternal()

    • Serializable在反序列化时,对象完全以它存储的二进制位为基础来构造,而不用调用构造器

    例:

    Blips.javaBlip3.java

    10.2.3 transient(瞬时)关键字

    如果我们正操作的是一个Serializable对象,那么所有序列化操作都会自动进行。为了能够予以控制,可以用transient(瞬时)关键字逐个字段地关闭序列化,它的意思是“不用麻烦你保存或恢复数据——我会自己处理的”。

    由于Externalizable对象在默认情况下不保存任何字段,所以transient关键字只能和Serializable对象一起使用。

    10.3 序列化的持久性

    我们可以通过一个字节数组来使用对象序列化,从而实现对任何可Serializable对象的“深度复制”(deep copy)——深度复制意味着我们复制的是整个对象网,而不仅仅是基本对象及其引用。

    • 一个对象被序列化在单一流中,就可以恢复出与我们写出时一样的对象网,并且没有任何意外重复复制出的对象。

    • 一个对象被序列化在不同流中,再从不同流恢复时,得到的对象地址不同。

    例:

    MyWorld.java

    对象序列化的一个重要限制是它只是Java的解决方案:只有Java程序才能反序列化这种对象。一种更具互操作性的解决方案是将数据转换为XML格式,这可以使其被各种各样的平台语言使用。

    相关文章:

    Java编程思想学习课时(三)第15章-泛型

    Java编程思想学习课时(四)第17章-容器深入探讨

以上がJavaプログラミング思考学習教室(5) 第18章 - Java IOシステムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。