1. データストリーム

プログラムがそれ自体が独立した島として存在することはほとんどありません。プログラムは通常、何らかの形で「外部世界」と対話します。これは、キーボードからのデータの読み取り、メッセージの送信、インターネットからのページのダウンロード、または逆にリモート サーバーへのファイルのアップロードによって発生する可能性があります。

これらすべての動作を一言で表すと、「プログラムと外部世界との間のデータ交換」となります。待ってください、それはただの一言ではありません。

もちろん、データ交換自体は、データの受信とデータの送信の 2 つの部分に分けることができます。たとえば、Scannerオブジェクトを使用してキーボードからデータを読み取ります。これはデータの受信です。コマンドを使用してデータを画面に表示しますSystem.out.println()。これがデータの送信です。

プログラミングでは、「ストリーム」という用語はデータ交換を表すために使用されます。その用語はどこから来たのでしょうか?

実生活では、水の流れや意識の流れが存在します。プログラミングにはデータ ストリームがあります。

ストリームは多用途のツールです。これらにより、プログラムはどこからでもデータを受信し (入力ストリーム)、どこにでもデータを送信できます (出力ストリーム)。したがって、次の 2 つのタイプがあります。

  • 入力ストリームはデータを受信するためのものです
  • 出力ストリームはデータを送信するためのものです

ストリームを「有形」にするために、Java の作成者は 2 つのクラス、InputStreamおよび を作成しましたOutputStream

このクラスには、そこからデータを読み取るためのメソッドがInputStreamあります。read()このクラスには、データを書き込むためのメソッドがOutputStreamあります。write()他の方法もありますが、それについては後で詳しく説明します。

バイトストリーム

どのような種類のデータについて話しているのでしょうか? どのような形式が必要ですか? 言い換えれば、これらのクラスはどのようなデータ型をサポートしているのでしょうか?

これらはジェネリック クラスであるため、最も一般的なデータ型であるbyte. オブジェクトはOutputStreamバイト (およびバイト配列) を書き込むことができ、InputStreamオブジェクトはバイト (またはバイト配列) を読み取ることができます。それだけです。他のデータ型はサポートされていません。

そのため、これらのストリームはバイト ストリームとも呼ばれます。

ストリームの特徴の 1 つは、そのデータが連続的にのみ読み取り (または書き込み) できることです。ストリームの前にあるデータをすべて読み取らない限り、ストリームの途中からデータを読み取ることはできません。

これは、クラス全体でキーボードからのデータの読み取りがどのように行われるかですScanner。キーボードからデータを 1 行ずつ順番に読み取ります。ある行を読み、次に次の行を読み、さらに次の行を読みます。適切なことに、行を読み取るメソッドは と呼ばれますnextLine()

へのデータの書き込みOutputStreamもシーケンシャルに行われます。この良い例はコンソール出力です。1 行を出力し、その後に次の行が出力されます。シーケンシャル出力です。最初の行、次に 10 行目、次に 2 行目を出力することはできません。すべてのデータは出力ストリームに順番にのみ書き込まれます。

文字ストリーム

最近、文字列が 2 番目に人気のあるデータ型であることを学びましたが、実際にそうなのです。多くの情報が文字や文字列全体の形で受け渡されます。コンピューターはすべてをバイトとして送受信することに優れていますが、人間はそこまで完璧ではありません。

この事実を考慮して、Java プログラマはさらに 2 つのクラス、Readerと を作成しましたWriter。このReaderクラスは クラスに似ていますInputStreamが、そのread()メソッドはバイトではなく文字 ( char) を読み取ります。クラスはクラスWriterに対応しますOutputStream。また、クラスと同様にReader、バイトではなく文字 ( ) を扱いますchar

これら 4 つのクラスを比較すると、次のような図が得られます。

バイト (バイト) 文字 (char)
データの読み取り
InputStream
Reader
データの書き込み
OutputStream
Writer

実用化

InputStreamOutputStreamおよびクラス自体は、データを読み取ることができる(ReaderまたはWriterデータを書き込むことができる)具体的なオブジェクトに関連付けられていないため、誰にも直接使用されません。しかし、これら 4 つのクラスには、多くのことを実行できる子孫クラスがたくさんあります。


2.InputStreamクラス

このInputStreamクラスは、何百もの子孫クラスの親クラスであるため、興味深いものです。独自のデータはありませんが、すべての派生クラスが継承するメソッドがあります。

一般に、ストリーム オブジェクトが内部にデータを格納することはほとんどありません。ストリームはデータの読み取り/書き込みのためのツールですが、ストレージではありません。とはいえ、例外もあります。

InputStreamクラスとそのすべての子孫クラスのメソッド:

メソッド 説明
int read()
ストリームから 1 バイトを読み取ります
int read(byte[] buffer)
ストリームからバイト配列を読み取ります
byte[] readAllBytes()
ストリームからすべてのバイトを読み取ります
long skip(long n)
nストリーム内のバイトをスキップします(バイトを読み取って破棄します)。
int available()
ストリームに何バイト残っているかを確認します
void close()
ストリームを閉じます

これらの方法を簡単に説明します。

read()方法

このメソッドはストリームから1 バイトread()を読み取り、それを返します。戻り値の型に混乱するかもしれません。この型が選択されたのは、標準の整数型であるためです。の最初の 3 バイトはゼロになります。intintint

read(byte[] buffer)方法

これは、このメソッドの 2 番目の変形ですread()。これにより、バイト配列をInputStream一度に読み取ることができます。バイトを格納する配列を引数として渡す必要があります。このメソッドは、実際に読み取られたバイト数を表す数値を返します。

10 キロバイトのバッファーがあり、FileInputStreamクラスを使用してファイルからデータを読み取るとします。ファイルに 2 キロバイトしか含まれていない場合、すべてのデータがバッファ配列にロードされ、メソッドは数値 2048 (2 キロバイト) を返します。

readAllBytes()方法

とても良い方法です。データがなくなるまですべてのデータを読み取りInputStream、単一バイト配列として返します。これは小さなファイルを読み取るのに非常に便利です。大きなファイルは物理的にメモリに収まらない可能性があり、メソッドは例外をスローします。

skip(long n)方法

このメソッドを使用すると、オブジェクトの最初の n バイトをスキップできますInputStream。データは厳密に順次に読み取られるため、このメソッドはストリームから最初の n バイトを単純に読み取り、それらを破棄します。

実際にスキップされたバイト数を返します (バイトがnスキップされる前にストリームが終了した場合)。

int available()方法

このメソッドはストリームにまだ残っているバイト数を返します。

void close()方法

このclose()メソッドはデータ ストリームを閉じ、それに関連付けられている外部リソースを解放します。ストリームが閉じられると、それ以上データを読み取ることはできなくなります。

非常に大きなファイルをコピーするプログラム例を書いてみましょう。readAllBytes()ファイル全体をメモリに読み取るメソッドは使用できません。例:

コード ノート
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreamファイルからの
OutputStream読み取り用 ファイルへの書き込み用

データを読み取るバッファー
ストリームにデータがある限り

データをバッファーに読み取る
データをバッファーから 2 番目のストリームに書き込む

この例では、2 つのクラスを使用しました。 は、ファイルからデータを読み取るためのFileInputStreamの子孫であり、 は、ファイルへのデータを書き込むためのの子孫です。2 番目のクラスについては少し後で説明します。InputStreamFileOutputStreamOutputStream

ここでのもう 1 つの興味深い点はreal変数です。データの最後のブロックがファイルから読み取られるとき、そのデータは 64KB 未満になる可能性があります。したがって、バッファ全体ではなく、その一部、つまり最初のrealバイトだけを出力する必要があります。これはまさにメソッド内で起こっていることですwrite()



3.Readerクラス

このReaderクラスは、クラスの完全な類似物ですInputStream。唯一の違いは、charバイトではなく文字 ( ) で動作することです。クラスと同様にInputStream、このReaderクラスも単独で使用されることはありません。これは数百の子孫クラスの親クラスであり、すべての子孫クラスに共通のメソッドを定義します。

Readerクラス (およびそのすべての子孫クラス)のメソッド:

メソッド 説明
int read()
charストリームから1 つを読み取ります
int read(char[] buffer)
charストリームから配列を読み取ります
long skip(long n)
ストリーム内をスキップしますn chars(読み取りと破棄)
boolean ready()
ストリームに何かがまだ残っているかどうかを確認します
void close()
ストリームを閉じます

メソッドはInputStreamクラスのメソッドと非常に似ていますが、若干の違いがあります。

int read()方法

このメソッドはcharストリームから 1 つを読み取り、それを返します。型charは に拡張されますintが、結果の最初の 2 バイトは常に 0 です。

int read(char[] buffer)方法

これは、このメソッドの 2 番目の変形ですread()。これにより、char 配列をReader一度に読み取ることができます。文字を格納する配列を引数として渡す必要があります。このメソッドは、実際に読み取られた文字数を表す数値を返します。

skip(long n)方法

このメソッドを使用すると、オブジェクトから最初の n 文字をスキップできますReader。これは、クラスの類似のメソッドとまったく同じように機能しますInputStream。実際にスキップされた文字数を返します。

boolean ready()方法

trueストリーム内に未読のバイトがある場合に返します。

void close()方法

このclose()メソッドはデータ ストリームを閉じ、それに関連付けられている外部リソースを解放します。ストリームが閉じられると、それ以上データを読み取ることはできなくなります。

比較のために、テキスト ファイルをコピーするプログラムを作成してみましょう。

コード ノート
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerファイルからの
Writer読み取り用 ファイルへの書き込み用

データを読み取るバッファー
ストリーム内にデータがある限り

データをバッファーに読み取る
データをバッファーから 2 番目のストリームに書き込む