Skip to content

シリアライゼーション完全ガイド

シリアライゼーション完全ガイド

Section titled “シリアライゼーション完全ガイド”

Javaのシリアライゼーション(直列化)の仕組みと実践的な使用方法を、実務で使える実装例とベストプラクティスとともに詳しく解説します。

シリアライゼーションは、オブジェクトをバイトストリームに変換するプロセスです。

シリアライゼーションの用途
├─ オブジェクトの永続化
├─ ネットワーク通信
├─ 分散システム
└─ キャッシング
import java.io.*;
// Serializableインターフェースを実装
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// シリアライゼーション
public void serialize(String filename) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(this);
}
}
// デシリアライゼーション
public static User deserialize(String filename)
throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
return (User) ois.readObject();
}
}
}
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
// デフォルトコンストラクタが必要
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getters and setters
}
import java.io.Serializable;
public class User implements Serializable {
// serialVersionUIDを明示的に定義(推奨)
private static final long serialVersionUID = 1L;
private String name;
private int age;
// クラスの構造が変更された場合、serialVersionUIDを更新
// private static final long serialVersionUID = 2L;
}

3. カスタムシリアライゼーション

Section titled “3. カスタムシリアライゼーション”
import java.io.*;
public class CustomSerialization implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient String password; // シリアライズしない
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject(); // デフォルトのシリアライゼーション
oos.writeObject(encrypt(password)); // カスタム処理
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // デフォルトのデシリアライゼーション
password = decrypt((String) ois.readObject()); // カスタム処理
}
private String encrypt(String data) {
// 暗号化処理
return data;
}
private String decrypt(String data) {
// 復号化処理
return data;
}
}
import java.io.*;
public class ExternalizableExample implements Externalizable {
private String name;
private int age;
// デフォルトコンストラクタが必要
public ExternalizableExample() {}
public ExternalizableExample(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name); // 明示的に書き込む
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
name = in.readUTF(); // 明示的に読み込む
age = in.readInt();
}
}

5. シリアライゼーションのバージョン管理

Section titled “5. シリアライゼーションのバージョン管理”
import java.io.*;
public class VersionedClass implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
// 新しいフィールドを追加する場合
// private String email; // 新しいフィールド
// 後方互換性を保つための処理
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
// 古いバージョンのデータを読み込む場合の処理
try {
// 新しいフィールドの読み込みを試みる
// email = (String) ois.readObject();
} catch (EOFException e) {
// 古いバージョンのデータの場合
email = "default@example.com";
}
}
}
import java.io.*;
public class SerializationOptimization {
// オブジェクトを再利用
private static final ThreadLocal<ByteArrayOutputStream>
outputStreamPool = ThreadLocal.withInitial(
ByteArrayOutputStream::new);
public byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream baos = outputStreamPool.get();
baos.reset(); // リセット
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(obj);
return baos.toByteArray();
}
}
}

カスタムシリアライゼーションによる最適化

Section titled “カスタムシリアライゼーションによる最適化”
import java.io.*;
public class OptimizedSerialization implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
// カスタムシリアライゼーションでサイズを削減
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.writeUTF(name); // Stringを直接書き込む
oos.writeInt(age);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
name = ois.readUTF();
age = ois.readInt();
}
}

シリアライゼーションのリスク

Section titled “シリアライゼーションのリスク”
// 問題のあるコード: 信頼できないソースからのデシリアライゼーション
public class SecurityIssue {
public Object deserialize(byte[] data)
throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(data))) {
return ois.readObject(); // 危険: 任意のクラスを読み込める
}
}
}

安全なデシリアライゼーション

Section titled “安全なデシリアライゼーション”
import java.io.*;
public class SecureDeserialization {
// 許可されたクラスのリスト
private static final Set<String> ALLOWED_CLASSES =
Set.of("com.example.User", "com.example.Product");
public Object deserialize(byte[] data)
throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(data)) {
// カスタムObjectInputStreamでクラスを検証
return new SecureObjectInputStream(ois).readObject();
}
}
private static class SecureObjectInputStream
extends ObjectInputStream {
public SecureObjectInputStream(InputStream in)
throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!ALLOWED_CLASSES.contains(className)) {
throw new SecurityException(
"Class not allowed: " + className);
}
return super.resolveClass(desc);
}
}
}
import java.io.*;
public class PersistenceExample {
public void saveUser(User user, String filename)
throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(user);
}
}
public User loadUser(String filename)
throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
return (User) ois.readObject();
}
}
}
import java.io.*;
import java.net.*;
public class NetworkSerialization {
// サーバー側
public void server() throws IOException {
try (ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
ObjectOutputStream oos = new ObjectOutputStream(
socket.getOutputStream())) {
User user = new User("Alice", 25);
oos.writeObject(user);
}
}
// クライアント側
public void client() throws IOException, ClassNotFoundException {
try (Socket socket = new Socket("localhost", 8080);
ObjectInputStream ois = new ObjectInputStream(
socket.getInputStream())) {
User user = (User) ois.readObject();
System.out.println(user.getName());
}
}
}

シリアライゼーション完全ガイドのポイント:

  • Serializable: 基本的なシリアライゼーション
  • serialVersionUID: バージョン管理
  • カスタムシリアライゼーション: writeObject/readObject
  • Externalizable: 完全な制御
  • バージョン管理: 後方互換性
  • パフォーマンス最適化: オブジェクトプーリング、カスタムシリアライゼーション
  • セキュリティ: 安全なデシリアライゼーション

適切なシリアライゼーションにより、効率的なデータ永続化とネットワーク通信が可能になります。