これまでに、 IO API (入出力アプリケーション プログラミング インターフェイス) とjava.ioパッケージについて説明しました。これらのクラスは主に Java でストリームを操作するためのものです。ここで重要なのは、ストリームの概念です。

今日は、 NIO API (新しい入力/出力)について検討していきます。

I/O に対する 2 つのアプローチの主な違いは、IO API がストリーム指向であるのに対し、NIO API はバッファ指向であることです。したがって、理解すべき主な概念はバッファチャネルです。

バッファとは何ですか、チャネルとは何ですか?

チャネルデータが出入りする論理ポータルであり、バッファはこの送信データの送信元または送信先です。出力中、送信するデータはバッファーに入れられ、バッファーはデータをチャネルに渡します。入力中に、チャネルからのデータがバッファーに入れられます。

言い換えると:

  • バッファとは、単に情報を書き込んだり、情報を読み取ったりできるメモリのブロックです。
  • チャネルはファイルやソケットなどの I/O デバイスへのアクセスを提供するゲートウェイです。

チャネルは、java.io パッケージのストリームと非常によく似ています。どこにでも送信される (またはどこから送信される) すべてのデータは、チャネル オブジェクトを通過する必要があります。一般に、NIO システムを使用するには、I/O エンティティへのチャネルとデータを保存するためのバッファを取得します。次に、バッファーを操作して、必要に応じてデータを入力または出力します。

バッファ内で前後に移動する、つまりバッファ内を「歩く」ことができますが、これはストリームでは実行できませんでした。これにより、データ処理時の柔軟性が向上します。標準ライブラリでは、バッファは抽象Bufferクラスとその子孫のいくつかによって表されます。

  • バイトバッファ
  • 文字バッファ
  • ショートバッファ
  • IntBuffer
  • フロートバッファ
  • ダブルバッファ
  • ロングバッファ

サブクラス間の主な違いは、サブクラスが格納するデータ型 ( bytesintslongsおよびその他のプリミティブ データ型) です。

バッファのプロパティ

バッファには 4 つの主要なプロパティがあります。これらは、容量、制限、位置、およびマークです。

容量は、バッファに保存できるデータ/バイトの最大量です。バッファの容量は変更できません。バッファがいっぱいになると、さらに書き込む前にバッファをクリアする必要があります。

書き込みモードでは、バッファの制限はその容量と同じであり、バッファに書き込むことができるデータの最大量を示します。読み取りモードでは、バッファの制限は、バッファから読み取ることができるデータの最大量を指します。

位置は、バッファ内のカーソルの現在位置を示します。バッファの作成時には、最初は 0 に設定されます。つまり、次に読み書きされる要素のインデックスです。

マークカーソル位置を保存するために使用されます。バッファを操作すると、カーソルの位置は常に変化しますが、いつでも前にマークした位置に戻すことができます。

バッファを操作するためのメソッド

次に、チャネルとの間でデータを読み書きするためにバッファ (メモリ ブロック) を操作できるメソッドの主要なセットを見てみましょう。

  1. assign(int Capacity) — このメソッドは、指定された容量で新しいバッファを割り当てるために使用されます。渡された容量が負の整数の場合、allocate ()メソッドはIllegalArgumentExceptionをスローします。

  2. Capacity()は現在のバッファの容量を返します。

  3. Position()は現在のカーソル位置を返します。読み取りおよび書き込み操作では、カーソルがバッファの最後に移動します。戻り値は常に制限以下になります。

  4. limit()は現在のバッファの制限を返します。

  5. mark() は、現在のカーソル位置をマーク (保存) するために使用されます。

  6. reset() は、カーソルを以前にマークした (保存した) 位置に戻します。

  7. clear() は位置をゼロに設定し、容量の制限を設定します。このメソッドはバッファ内のデータをクリアしません。位置、リミット、マークのみが再初期化されます。

  8. flick() はバッファを書き込みモードから読み取りモードに切り替えます。また、現在の位置にリミットを設定し、その位置をゼロに戻します。

  9. read() — チャネルの read メソッドはチャネルからバッファにデータを書き込むために使用され、バッファのput()メソッドはデータをバッファに書き込むために使用されます。

  10. write() — チャネルの write メソッドはバッファからチャネルにデータを書き込むために使用され、バッファのget()メソッドはバッファからデータを読み取るために使用されます。

  11. rewind() はバッファを巻き戻します。このメソッドは、バッファを再読み込みする必要がある場合に使用されます。位置がゼロに設定され、制限は変更されません。

次に、チャネルについて少しお話します。

Java NIO での最も重要なチャネル実装は次のクラスです。

  1. FileChannel — ファイルとの間でデータを読み書きするためのチャネル。

  2. DatagramChannel — このクラスは、UDP (ユーザー データグラム プロトコル) 経由でネットワーク上でデータの読み取りと書き込みを行います。

  3. SocketChannel — TCP (伝送制御プロトコル) 経由でネットワーク上でデータを読み書きするためのチャネル。

  4. ServerSocketChannel — Web サーバーと同様に、TCP 接続を介してデータを読み書きするためのチャネル。SocketChannel受信接続ごとに作成されます。

練習

数行のコードを記述します。まず、ファイルを読み取り、その内容をコンソールに表示してから、ファイルに文字列を書き込みます。

コードには多くのコメントが含まれています。すべてがどのように機能するかを理解するのに役立つことを願っています。


// Create a RandomAccessFile object, passing in the file path
// and a string that says the file will be opened for reading and writing
try (RandomAccessFile randomAccessFile = new RandomAccessFile("text.txt", "rw");
    // Get an instance of the FileChannel class
    FileChannel channel = randomAccessFile.getChannel();
) {
// Our file is small, so we'll read it in one go   
// Create a buffer of the required size based on the size of our channel
   ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
   // Read data will be put into a StringBuilder
   StringBuilder builder = new StringBuilder();
   // Write data from the channel to the buffer
   channel.read(byteBuffer);
   // Switch the buffer from write mode to read mode
   byteBuffer.flip();
   // In a loop, write data from the buffer to the StringBuilder
   while (byteBuffer.hasRemaining()) {
       builder.append((char) byteBuffer.get());
   }
   // Display the contents of the StringBuilder on the console
   System.out.println(builder);
 
   // Now let's continue our program and write data from a string to the file
   // Create a string with arbitrary text
   String someText = "Hello, Amigo!!!!!";
   // Create a new buffer for writing,
   // but let the channel remain the same, because we're going to the same file
   // In other words, we can use one channel for both reading and writing to a file
   // Create a buffer specifically for our string — convert the string into an array and get its length
   ByteBuffer byteBuffer2 = ByteBuffer.allocate(someText.getBytes().length);
   // Write our string to the buffer
   byteBuffer2.put(someText.getBytes());
   // Switch the buffer from write mode to read mode
   // so that the channel can read from the buffer and write our string to the file
   byteBuffer2.flip();
   // The channel reads the information from the buffer and writes it to our file
   channel.write(byteBuffer2);
} catch (FileNotFoundException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

NIO APIを試してみてください- きっと気に入っていただけるでしょう!