Revised: Feb./23rd/2003; Since: Jan./5th/2002
プログラムでは一般に、多くの種類のデータを、多くの種類の入力先から取得して、多くの種類の出力先へ書き込む必要が考えられます。 Java では、これらのデータの入出力 (I/O) は一般にストリームとして解釈することになります。入力元へのストリームを開いて情報を読み込み、出力先へのストリームを開いて情報を書き込み、必要が無くなればそれらのストリームを閉じます。
これらのストリームを扱うための標準クラスライブラリは、 java.io
パッケージから提供されます。これらのクラスは一般に、取り扱うデータの種類に応じて文字ストリームとバイトストリームの二つに分けられます。
そして、これらのストリームはオブジェクトとして扱われます。
ストリームとはデータの直線的な流れという概念であり、現代的なプログラミング言語では、入出力をストリームとして扱います。
例えば、ディスク上のファイルに向かうストリームがあるとき、そこに書き込んだデータはファイルに出力されます。ネットワークから流れ込むストリームからはネットワーク越しのデータを受け取ることができます。プログラマはストリームに対する入出力を考えるだけで、その流れ行く果ての、実デバイスに対しては考えなくて良くなります。
JavaではデータI/Oにストリームという概念を使います。java.ioパッケージ内のストリーム・クラスは、大きく分けると、バイト・ストリーム、文字ストリーム、オブジェクト・ストリームの三つなります。JavaのI/Oは、全てストリームのオブジェクトとして実装します。
java.ioパッケージ内のクラスは、デバイスI/O、ネットワークI/Oなどが統一的な枠組みで設計されています。全てのストリームは、バイト・ストリーム、文字ストリーム、オブジェクト・ストリームに分けられ、次の6つの抽象クラス/インタフェースに基づきます。
Javaでは、文字は2バイトのUNICODEで表現されます。この点で、文字列処理においては、バイトの連続(シーケンス)であるバイト・ストリームは効率が悪く、文字ストリームが要求されました。また、オブジェクトをファイルやDBに格納したり、ネットワーク越しにやり取りしたりするために、オブジェクト・ストリームが必要となりました。
また、J2SE v1.4では、さまざまな要件に応えてjava.nioパッケージが追加されました。今後何年も使われるだろうパッケージを、敢えてNew I/Oと命名したことに、ストリームの枠組みは変更しないで、拡張機能だけをnioに実装する意図が読めます。
まずjava.ioのクラス群の利用を検討し、解決できない要件/要求が発生したときに、ユーティリティとしてnioパッケージも検討してください。本稿ではv1.3と共通のjava.ioパッケージのクラス群について紹介します。
java.ioのストリーム・クラスは要件に応じて多岐に渡ります。バイトストリームはInputStream/OutputStream、文字ストリームはReader/Writerを継承しています。詳細はAPI仕様書をご覧いただくとして、ここではその全体を概観してみます。
バイト・ストリームと文字ストリームには、お互いにカウンター・パートとなるクラスが存在するものがあります。次に、代表的なストリームに関するそれぞれの対応を示します。
バイト・ストリーム | 文字ストリーム | 説明 | |
---|---|---|---|
入力 出力 | BufferedInputStream BufferedOutputStream | BufferedReader CharArrayReader | 入出力にバッファを使って効率を上げる |
入力 出力 | ByteArrayInputStream ByteArrayOutputStream | CharArrayReader CharArrayWriter StringWriter | バイト配列や文字配列、String型オブジェクトに対するストリームを作成する |
入力 出力 | FileInputStream FileOutputStream | FileReader FileWriter | ファイルに対するストリームを作成する |
入力 出力 | FilterInputStream FilterOutputStream | FilterReader FilterWriter | これらを継承してカスタムストリームを作成するための抽象クラス |
入力 出力 | PipeInputStream PipeOutputStream | PipeReader PipeWriter | 複数の異なるストリーム間をつなげるパイプを作成する |
java.io.*, java.swing.*などの代表的なストリームの階層構造は図3のようになります。非推奨のクラス、特殊なインタフェースやクラスなどは除いてあります。
![]() |
図3. ストリームの階層構造(IDG Java 2 J2SE 1.4より転載) |
---|
Java では、文字は Unicode で扱います。 Unicode で文字符号化された文字セットは一文字が16ビット(2バイト)で表現されます。そのコンピュータ上でのフォーマットは UTF と呼ばれます。因みに、 UTF は UCS Transformation Format であり、 UCS (Universal Character Set) を Transform した Format です。
下位7ビットは ASCII と互換性を持ちます。英米圏の ASCII で満足してきた人々は Unicode を Internationalization (I18N) を満足するものとして歓迎しています。しかし、日本や中国など、膨大な文字を必要とする言語圏での実用においては、多くの問題が指摘されており、必ずしも I18N のキラー・ソリューションではありません。
いずれにせよ、 JavaVM は文字/文字列の内部表現に Unicode を使い、入出力ではそのフォーマットである UTF を用います。
16ビット文字を読み書きするストリームは文字ストリームと呼ばれています。
文字ストリームにも多くの種類が存在しますが、それらは、Reader
クラスと Writer
クラスをスーパークラスとするサブクラスになっています。因みに、 Reader
/Writer
クラスは抽象クラスであり、直接インスタンス化することはできません。
java.io.Reader |
---|
Reader | +--BufferedReader------LineNumberReader | +--CharArrayReader | +--InputStreamReader---FileReader | +--FilterReader--------PushBackReader | +--PipeReader | +--StringReader |
java.io.Writer |
Writer | +--BufferedWriter | +--CharArrayWriter | +--OutputStreamWriter--FileWriter | +--FilterWriter | +--PipeWriter | +--StringWriter |
例えば、 Reader
クラスは読み込みのためのメソッドを次のように定義しています。
int read() int read(char cbuf[]) int read(char cbuf[], int offset, int length)
確かに16ビット文字の読み込みになっています。同じく、 Writer
クラスは書き込みのメソッドを次のように定義しており、16ビット文字を扱っていることが分かります。
int write(int c) int write(char cbuf[]) int write(char cbuf[], int offset, int length)
16ビット文字の I/O に対して、画像や音などのバイナリー・データを扱うストリームはバイトストリームと呼ばれています。これらのクラスは、 InputStream
クラスと OutputStream
から派生しています。
java.io.InputStream |
---|
InputStream | +--FileInputStream | +--LineNumberInputStream +--PipedInputStream | | +--DataInputStream +--FilterInputStream------| | +--BufferedInputStream +--ByteArrayInputStream | | +--PushbackInputStream +--SequenceInputStream | +--StringBufferInputStream | +--ObjectInputStream |
java.io.OutputStream |
OutputStream | +--FileOutputStream | +--PipedOutputStream +--DataOutputStream | | +--FilterOutputStream-----+--BufferedOutputStream | | +--ByteArrayOutputStream +--PrintStream | +--ObjectOutputStream |
Reader
/Writer
と同じく、 InputStream
/OutputStream
の読み込み/書き込みのメソッドを見てみましょう。
最初は InputStream
クラスで定義されている読み込みのメソッドです:
int read() int read(byte cbuf[]) int read(byte cbuf[], int offset, int length)
次に OutputStream
の書き込みのメソッドの定義を見てみます。
int write(int c) int write(byte cbuf[]) int write(byte cbuf[], int offset, int length)
どちらもバイト型でデータを取り扱っていることが分かります。