2-4-4: オブジェクトの入出力


・ 「シリアライザビリティ(Serializability)」というのは耳慣れない用語ですが、 Javaのオブジェクトを直接ストリームに入出力していまおうというアイディアです。 オブジェクトを「そのまま」ファイルに保存したり、 ネットワークのストリームを通じてやり取りすることが可能になります。
 最初にストリームに 入出力されるオブジェクトのクラス Student.java から設計しましょう。 シリアライザビリティの対象となるオブジェクトは、 必ず java.io.Serializable というインターフェイスを実装しなければなりません。 ただしその実装は全く形式的なものです。 Serializableは「シリアライザビリティの対象とするかどうか」を識別するため だけの目的に存在します。新しいメソッドの実装は要求しません。
 サンプルのクラスでは Student は非常に単純なクラスです。 3個のフィールドが存在し、学生の番号(number)と名前(name)、 その他の特記事項(etc)を記憶します。
 シリアライズされる情報は、 クラスを識別するためのヘッダ情報とオブジェクト固有のフィールドの情報です。 また、すべてのフィールドがシリアライザビリティの対象になるわけではありません。 フィールド自体がシリアライズできないオブジェクトの場合には、 当然ですがシリアライズの対象とは見なされません。 またシリアライズ可能でも対象にする必要がないフィールドは、 transient修飾子を付けることで対象から外すことができます。 Studentクラスでは、number と name はシリアライザビリティの対象ですが、 その他の情報を表すフィールド etc は対象外ということになります。

・ 次に実際にオブジェクトをストリームに入出力するサンプル StudentSaver.javaStudentLoader.java を作ってみましょう。 ストリームの対象としては簡単のため、ここでは "Student.ser" という 名前が固定されたファイルを用いることにします。
 オブジェクトのデータを書き出すためのストリームのクラスが ObjectOutputStreamです。 ObjectOutputStream は他の OutputStream のオブジェクトを元にして生成します。 ここでは指定された Fileに対応する FileOutputStreamのオブジェクトから生成しています。 オブジェクトの書き出しは writeObject()メソッドで行います。 パターンは通常のデータの場合とほとんど同じであることがわかるでしょう。
 サンプルが長くなっているのは、むしろ後半の main()メソッドのせいです。 ここでは標準入力から対話的に Studentのフィールドのデータを入力します。 BufferdReader の使い方は先に説明したとおりです。 saveStudent()メソッドの処理は static で定義され、 いつでも簡単に呼び出せるようにしました。 また入力の時に直接配列に記憶せず java.util.Vectorクラスが利用されています。 これは入力されるオブジェクトの件数があらかじめ予測できないからです。 Vector は配列と違って要素を自由に追加していくことが可能です。
 StudentSaverのプログラムで入力された結果のファイル Student.ser はテキスト形式ではありません。 したがって通常のエディタで内容を見ることはできません。 StudentLoaderを用いて内容を確認します。
 オブジェクトの読み込みには ObjectInputStream というストリームのクラスを 利用します。このクラスのオブジェクトは、やはり FileInputStreamなどの オブジェクトを元にして生成されます。 オブジェクトを読み込むには readObject()メソッドを用います。 上のサンプルは他のクラスからも利用できる static メソッド loadStudent()によって Studentクラスの配列にデータを読み込みます。 結果は単純に標準出力に表示されます。 先の StudentSaverと併せて実行してみてください。
 これらのサンプルで見る限りでは シリアライザビリティのメリットがわかりにくいかもしれません。 2つのフィールドの値を個別にファイルに入出力しても同じように思えます。 (むしろテキストファイルに保存できた方が通常のエディタで編集できて 便利かもしれません。) しかし、オブジェクトが複雑な構造を持つ場合には事情が違ってきます。 上のサンプルに見られるストリームへの入出力部分は、 保存すべきフィールドの数やデータの型が増えても基本的に変わりません。 シリアライザビリティの対象となるオブジェクト自身の内容が変更されても、 ソースを書き直すことなく対応できるようになっています。 (ここでは詳しく触れませんが、オブジェクトのバージョンを識別する機能も あります。) アプリケーションがオブジェクトの情報を入出力する部分は 著しく簡略化できるわけです。
 その他に副次的なメリットとして、日本語のシステム依存がなくなるという 点も指摘しておきましょう。 このサンプルのように学生の名前に日本語のデータが使用されている場合には、 通常のテキストファイルでは SJISや EUCのようにシステムごとに異なる 日本語コードが使われています。当然のことながら異なるシステムでプログラムを 実行する場合には正しく日本語を読み取ることができません。 しかし Serializabirityによってファイルの保存されたオブジェクトは、 文字列の情報を Unicode で表現された Stringオブジェクトとして持っているため、 システムに関係なく常に正しい日本語のデータを読み取ることが可能になります。