0Pricing
Java Academy · レッスン

カスタムシリアライズ:writeObject と readObject

writeObject と readObject をオーバーライドし、暗号化、圧縮、カスタムフィールド処理を実装します。

「カスタムシリアライズ:writeObject と readObject」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

デフォルトシリアライズでは不十分な場合

デフォルトシリアライズでは、transientでないすべてのフィールドがそのまま保存されます。暗号化、圧縮、または別の表現が必要になる場合もあります。そのようなときにwriteObjectとreadObjectのフックを使用します。

writeObjectの宣言

private void writeObject(ObjectOutputStream oos)メソッドを追加します。Javaはデフォルトの仕組みの代わりにこのメソッドを呼び出します。何を書き込むかを正確に制御できます。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject(); // write non-transient fields normally
    oos.writeUTF(encrypt(this.secret)); // write encrypted secret
}

readObjectの宣言

private void readObject(ObjectInputStream ois)メソッドを追加します。Javaはデシリアライズ中にこのメソッドを呼び出します。書き込んだときとまったく同じ順序で読み取る必要があります。

private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject(); // restore non-transient fields
    this.secret = decrypt(ois.readUTF()); // decrypt and restore
}

順序が重要

writeObjectで書き込む内容は、readObjectで同じ順序で読み取らなければなりません。順序が一致しないとStreamCorruptedExceptionが発生します。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.writeInt(version);
    oos.writeUTF(data);
}
private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    this.version = ois.readInt();  // same order
    this.data    = ois.readUTF();
}

シリアライズ中のデータ圧縮

writeObject内でストリームをGZIPOutputStreamでラップして大きなフィールドを圧縮し、続いてreadObject内でGZIPInputStreamを使って展開します。

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try (GZIPOutputStream gz = new GZIPOutputStream(bos)) {
        gz.write(largePayload.getBytes());
    }
    oos.writeObject(bos.toByteArray());
}

readResolveフック

readResolve()はデシリアライズ後に呼び出されます。置き換えるオブジェクトを返すことで、シングルトンやenumにおいてデシリアライズ後もインスタンスが1つだけ存在するようにできます。

public class AppConfig implements Serializable {
    private static final AppConfig INSTANCE = new AppConfig();
    private AppConfig() {}
    public static AppConfig getInstance() { return INSTANCE; }
    private Object readResolve() { return INSTANCE; } // return singleton
}

writeReplaceフック

writeReplace()はシリアライズ前に呼び出されます。代わりにシリアライズする別のオブジェクトを返します。プロキシパターンやシリアライズ用サロゲートに便利です。

private Object writeReplace() {
    return new UserProxy(this.id); // serialize a lightweight proxy
}

readObjectでのフィールド検証

readObject内に検証ロジックを追加し、多層防御の一環として不正または改ざんされたデータを拒否できます。

private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    if (age < 0 || age > 150) {
        throw new InvalidObjectException("Invalid age: " + age);
    }
}

バージョン変更への対応

writeObjectでバージョンフィールドを使用すると、移行中にreadObjectで新旧両方の形式を適切に処理できます。

private static final int SERIAL_VERSION = 2;
private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();
    oos.writeInt(SERIAL_VERSION);
    if (SERIAL_VERSION >= 2) oos.writeUTF(email); // new field
}
private void readObject(ObjectInputStream ois)
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    int v = ois.readInt();
    if (v >= 2) this.email = ois.readUTF();
}

Serialization Proxyパターン

堅牢な方法として、最小限の状態を保持するprivateなstatic内部クラスを作成し、writeReplaceでそのプロキシを書き込み、プロキシのreadResolveで元のオブジェクトを再構築します。

パフォーマンスに関する考慮事項

カスタムシリアライズではメソッド呼び出しのオーバーヘッドが発生しますが、ペイロードサイズを大幅に削減できる場合があります。最適化の前にベンチマークを実施し、最大限の制御が必要な場合はExternalizableを使用してください。

クイックチェック

writeObjectとreadObjectで、順序について何が成り立っていなければなりませんか。

まとめ

暗号化、圧縮、またはカスタム形式を扱うにはwriteObjectとreadObjectをオーバーライドします。シングルトンにはreadResolveを使用します。必ず、書き込んだ順序と同じ順序で読み取ってください。

よくある質問

「カスタムシリアライズ:writeObject と readObject」レッスンは無料ですか?

はい。「カスタムシリアライズ:writeObject と readObject」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「カスタムシリアライズ:writeObject と readObject」で何を学びますか?

writeObject と readObject をオーバーライドし、暗号化、圧縮、カスタムフィールド処理を実装します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「カスタムシリアライズ:writeObject と readObject」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Java シリアライズの基本
  2. transient フィールドと serialVersionUID
  3. カスタムシリアライズ:writeObject と readObject
  4. 最新の代替手段:JSON と Protocol Buffers
← Java Academyに戻る