私はこの質問が本当に古く、受け入れられた答えがあることを知っていますが、Google検索で非常に高く表示されるので、私が重要だと思う3つのケースをカバーする回答が提供されていないので、私は比較検討すると思いました-これらの主な用途メソッド。もちろん、すべてカスタムシリアライゼーションフォーマットが実際に必要であると想定しています。
たとえば、コレクションクラスを見てみましょう。リンクされたリストまたはBSTのデフォルトのシリアル化では、要素を順番にシリアル化するだけの場合に比べて、パフォーマンスがほとんど向上せず、スペースが大幅に失われます。コレクションがプロジェクションまたはビューの場合、これはさらに当てはまります。パブリックAPIによって公開されるよりも大きな構造への参照が保持されます。
シリアル化されたオブジェクトにカスタムシリアル化が必要な不変フィールドwriteObject/readObject
がある場合、で書き込まれたストリームの一部を読み取る前に非シリアル化オブジェクトが作成されるため、元のソリューションは不十分ですwriteObject
。リンクリストのこの最小限の実装を取り上げます。
public class List<E> extends Serializable {
public final E head;
public final List<E> tail;
public List(E head, List<E> tail) {
if (head==null)
throw new IllegalArgumentException("null as a list element");
this.head = head;
this.tail = tail;
}
//methods follow...
}
この構造はhead
、すべてのリンクのフィールドを再帰的に書き込み、その後にnull
値を書き込むことでシリアル化できます。ただし、このような形式の逆シリアル化は不可能になりreadObject
ます。メンバーフィールドの値を変更することはできません(現在はに修正されていますnull
)。ここにwriteReplace
/ readResolve
ペアが来ます:
private Object writeReplace() {
return new Serializable() {
private transient List<E> contents = List.this;
private void writeObject(ObjectOutputStream oos) {
List<E> list = contents;
while (list!=null) {
oos.writeObject(list.head);
list = list.tail;
}
oos.writeObject(null);
}
private void readObject(ObjectInputStream ois) {
List<E> tail = null;
E head = ois.readObject();
if (head!=null) {
readObject(ois); //read the tail and assign it to this.contents
this.contents = new List<>(head, this.contents)
}
}
private Object readResolve() {
return this.contents;
}
}
}
上記の例がコンパイル(または動作)しない場合は申し訳ありませんが、うまくいけば私のポイントを説明するのに十分です。これが非常にフェッチされた例であると思われる場合は、多くの関数型言語がJVMで実行され、このアプローチが彼らの場合に不可欠になることを覚えておいてください。
実際にに書き込んだクラスとは異なるクラスのオブジェクトを逆シリアル化したい場合がありますObjectOutputStream
。これはjava.util.List
、より長いスライスを公開するリスト実装などのビューの場合ArrayList
です。明らかに、バッキングリスト全体をシリアル化することは悪い考えであり、表示されたスライスからのみ要素を書き込む必要があります。しかし、なぜそれで停止し、逆シリアル化の後に無意味なレベルの間接参照があるのですか?ArrayList
ビュークラスでラップする代わりに、ストリームからに要素を読み込んで直接返すことができます。
または、シリアル化専用の同様のデリゲートクラスを用意することもできます。良い例は、シリアル化コードの再利用です。たとえば、ビルダークラス(StringBuilderのStringBuilderに類似)がある場合、ストリームに空のビルダーを書き込み、コレクションのサイズとコレクションのイテレーターによって返される要素を続けて、任意のコレクションをシリアル化するシリアル化デリゲートを記述できます。逆シリアル化には、ビルダーを読み取り、その後に読み取ったすべての要素を追加build()
し、デリゲートからfinalの結果を返すことが含まれreadResolve
ます。その場合は、コレクション階層のルートクラスでのみシリアル化を実装する必要があります。抽象iterator()
およびbuilder()
メソッド(同じタイプのコレクションを再作成するための後者-これ自体は非常に便利な機能です)。別の例としては、コードを完全に制御できないクラス階層があります-サードパーティのライブラリの基本クラスには、何も知らないプライベートフィールドがいくつもあり、バージョンによって異なる可能性があります。シリアル化されたオブジェクト。その場合は、データを書き込み、逆シリアル化でオブジェクトを手動で再構築する方が安全です。
String.CaseInsensitiveComparator.readResolve()