オブジェクトの直列化
こんにちは。「Javaを復習する初心者」です。今回はオブジェクトをファイルに保存する方法を復習しました。
実際に試したことは「オブジェクトをファイルに保存する」ことと、「保存したオブジェクトを読み取る」ことです。
保存
保存するテストを行うために、次の保存用クラスを作成しました。
package practice.test20160826; import java.io.Serializable; public class Student implements Serializable { private String name; private int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
ファイルにオブジェクトを保存するにはObjectOutputStreamを使います。ObjectOutputStreamクラスにはwriteObjectというメソッドが用意されています。この時、保存するオブジェクトはクラスの定義でSerializableインターフェースを実装している必要があります。このインターフェースを実装していない場合、保存のときに例外が発生します。NotSerializableExceptionがスローされます。
実際に保存を実行するクラスは次のように作りました。
package practice.test20160825; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class HelloWrite { public static void main(String[] args) { try (FileOutputStream file = new FileOutputStream("resource/student.txt"); ObjectOutputStream stream = new ObjectOutputStream(file);){ stream.writeInt(2016); stream.writeInt(8); stream.writeInt(25); stream.writeObject(new Student("name1", 20)); stream.writeObject(new Student("name2", 21)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
ObjectOutputStreamクラスのコンストラクタにFileOutputStreamクラスのインスタンスを渡しています。FileOutputStreamクラスのコンストラクタはファイル名を引数に持つものを使いました。resourceフォルダのstudent.txtというファイルに保存することにしました。インスタンスの生成をtry-catch-resource文を使って記述しています。この構文では自動的にclose処理を行ってくれます。
最初はオブジェクトの保存のみを考えていたのですが、プリミティブ型はどうするのだろうと思い、API仕様書を確認しました。プリミティブ型に対応するメソッドが用意されていることが分かりました。例えば、int型に対してはvoid writeInt(int val)メソッドが用意されてます。オブジェクトの保存にはvoid writeObject(Object obj)メソッドを使います。
上記のプログラムで32ビットのint型である「2016」、「8」、「25」とStudentクラスのインスタンス2つをresource/student.txtに書き込みができました。
読み取り
読み込む方は以下のプログラムです。
package practice.test20160825; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream; public class HelloRead { public static void main(String[] args) { try (FileInputStream fileInputStream = new FileInputStream("resource/student.txt"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);){ int year = objectInputStream.readInt(); int month = objectInputStream.readInt(); int day = objectInputStream.readInt(); System.out.printf("%d年%2d月%2d日", year, month, day); System.out.println(); Student student = (Student)objectInputStream.readObject(); System.out.printf("name:%s, age:%d", student.getName(), student.getAge()); System.out.println(); student = (Student)objectInputStream.readObject(); System.out.printf("name:%s, age:%d", student.getName(), student.getAge()); System.out.println(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
実行すると以下のように出力されます。
2016年 8月25日 name:name1, age:20 name:name2, age:21
保存したデータを読み込むためはObjectInputStreamクラスを使います。保存時に最初の3つはint型を保存したので、int readInt()メソッドで読み込んでいます。オブジェクトの読み込みはreadObject()メソッドを使って返却値をキャストしています。使うメソッドの順番は書き込み時と対応している必要があります。例えば、最初にreadBoolean()メソッドを使うとOptionalDataExceptionが発生します。また、余計な分を読み込もうとするとエラーになります。例えば、今回の場合では、2つのオブジェクトを読み込んだ後にreadBoolean()メソッドを使うとEOFExceptionが発生します。
catch節に記述しているClassNotFoundExceptionは直列化されたオブジェクトのクラスが見つからなかった場合に発生します。冒頭で作ったStudent.javaをStudenta.javaというように名前を変えておくと発生します。