Javaオブジェクト直列化: オブジェクトの持続化と復元(Serializable/transient/ObjectOutputStream)
Javaはオブジェクト直列化というメカニズムを提供します。これはオブジェクトをバイトシーケンスとして表現し、オブジェクトのデータだけでなく、オブジェクトの型やオブジェクトに格納されているデータの型に関する情報も含みます。
直列化されたオブジェクトがファイルに書き込まれた後、そのファイルから読み取り、逆直列化(デシリアライズ)できます。つまり、型情報とオブジェクトおよびそのデータを表すバイトを使用して、メモリ内でオブジェクトを再作成できます。
最も印象的なのは、このプロセス全体がJVM非依存であることです。つまり、あるプラットフォームでオブジェクトを直列化し、まったく異なるプラットフォームで逆直列化できます。
ObjectInputStreamとObjectOutputStreamクラスは、オブジェクトの直列化と逆直列化のメソッドを含む高レベルストリームです。
ObjectOutputStreamクラスには様々なデータ型を書き込むための多くのwriteメソッドがありますが、特に際立っているメソッドが1つあります。
public final void writeObject(Object x) throws IOException
上記メソッドはObjectを直列化し、出力ストリームに送信します。同様に、ObjectInputStreamクラスにはオブジェクトの逆直列化のための以下のメソッドがあります。
public final Object readObject() throws IOException, ClassNotFoundException
このメソッドはストリームから次のObjectを取得し、逆直列化します。戻り値はObjectなので、適切なデータ型にキャストする必要があります。
Javaでの直列化の仕組みを示すために、本書の前半で説明したEmployeeクラスを使用します。以下のEmployeeクラスはSerializableインターフェースを実装しているとします。
例: Serializable実装クラス
public class Employee implements java.io.Serializable {
public String name;
public String address;
public transient int SSN;
public int number;
public void mailCheck() {
System.out.println("Mailing a check to " + name + " " + address);
}
}
クラスが正常に直列化されるには、以下の2条件を満たす必要があります。
- クラスが
java.io.Serializableインターフェースを実装していること。 - クラスのすべてのフィールドが直列化可能であること。直列化不可能なフィールドはtransientでマークする必要がある。
Java標準クラスが直列化可能かどうか知りたい場合は、そのクラスのドキュメントを確認してください。テストはシンプルです:クラスがjava.io.Serializableを実装していれば直列化可能、そうでなければ不可です。
オブジェクトの直列化
ObjectOutputStreamクラスを使用してオブジェクトを直列化します。以下のSerializeDemoプログラムはEmployeeオブジェクトをインスタンス化し、ファイルに直列化します。
プログラムが実行されると、employee.serという名前のファイルが作成されます。プログラムは出力を生成しませんが、コードを研究してプログラムが何をしているか判断してみてください。
注意 - オブジェクトをファイルに直列化する際、Javaの標準的な慣習としてファイルに.ser拡張子を付けます。
例: SerializeDemo
import java.io.*;
public class SerializeDemo {
public static void main(String[] args) {
Employee e = new Employee();
e.name = "Reyan Ali";
e.address = "Phokka Kuan, Ambehta Peer";
e.SSN = 11122333;
e.number = 101;
try {
FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
} catch (IOException i) {
i.printStackTrace();
}
}
}
オブジェクトの逆直列化
以下のDeserializeDemoプログラムはSerializeDemoプログラムで作成されたEmployeeオブジェクトを逆直列化します。プログラムを研究して出力を予測してみてください。
例: DeserializeDemo
import java.io.*;
public class DeserializeDemo {
public static void main(String[] args) {
Employee e = null;
try {
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("SSN: " + e.SSN);
System.out.println("Number: " + e.number);
}
}
出力
Deserialized Employee...
Name: Reyan Ali
Address: Phokka Kuan, Ambehta Peer
SSN: 0
Number: 101
以下の重要なポイントに注意してください。
- try/catchブロックは
readObject()メソッドで宣言されるClassNotFoundExceptionを捕捉しようとします。JVMがオブジェクトを逆直列化するには、そのクラスのバイトコードを見つけられる必要があります。逆直列化中にクラスが見つからない場合、ClassNotFoundExceptionをスローします。 readObject()の戻り値がEmployee参照にキャストされていることに注目。SSNフィールドの値はオブジェクト直列化時に11122333でしたが、このフィールドがtransientであるため、この値は出力ストリームに送信されませんでした。逆直列化されたEmployeeオブジェクトのSSNフィールドは0(intのデフォルト値)になります。
Java
- Javaのfinalキーワード完全解説:変数、メソッド、クラスでの活用法
- Java instanceof演算子完全ガイド:基本から継承・インターフェースまで
- Java継承のマスター:概念、種類、実践例
- Java Staticネストクラスの理解:用法、違い、実例
- Java無名インナークラス完全ガイド:定義、構文、実践例
- Javaシングルトンパターン完全解説:安全な実装と活用法
- Javaリフレクション完全ガイド:実行時のクラス・メソッド・フィールド検査
- Java ObjectOutputStream完全ガイド:シリアル化、メソッド、実践例
- Javaジェネリクス完全ガイド:再利用可能で型安全なコードの構築
- Javaファイル操作のマスター:java.ioによる作成・読み取り・書き込み・削除
- Javaオブジェクトとクラス:コアOOP概念を完全理解