7

Java クラスがSerializableインターフェースを実装しているがパブリック メソッドを持たない場合clone()、通常は次のようなディープ コピーを作成できます。

class CloneHelper {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            T copy = (T) ois.readObject();
            ois.close();
            return copy;
        } catch (ClassNotFoundException ex) {
            // Shouldn't happen
            throw new Error(ex);
        } catch (IOException ex) {
            // Probably a bug in T's custom serialization methods
            throw new RuntimeException(ex);
        }
    }
}

私はこのようなサードパーティのライブラリ クラスによく遭遇し、上記のようなハックに頼っています。ObjectOutputStream場合によっては、コピーを浅くするために拡張することさえありました。非効率的であること以外に、重大な問題が発生したことはありません (エンコード/デコードが遅く、一時的なシリアル化グラフが大量のメモリを消費する可能性があります)。

この手法を安全に使用できない場合は、おそらくクラスを宣言すべきではありませんSerializable

だから私が知りたいのは、あなたのクラスSerializableが.clone()Cloneable


関連: Java でオブジェクトをコピーする

4

7 に答える 7

5

上記のメカニズムを使用するよりも、コピー コンストラクターを使用することをお勧めします。何を深くまたは浅くコピーするかをより細かく定義し、オブジェクトのコピーをオブジェクトのシリアル化と区別することができます。コピー コンストラクターは、(たとえば) 2 つのオブジェクトがマスター オブジェクトへの参照を共有できるようにすることができますが、これはシリアル化されてネットワーク経由で送信されるオブジェクトには適していない場合があります。

このCloneableメソッドは現在、壊れていると広くみなされていることに注意してください。詳細については、Joshua Bloch のこの記事を参照してください。特にメソッドはありませんclone()

于 2010-01-24T18:38:59.207 に答える
4

Cloneableに関するBrianの指摘は非常に優れていますが、Cloneableが正しく機能したとしても、オブジェクトをシリアル化できるがクローン化できないようにしたい場合があります。

データベースレコードのメモリ内表現のように、オブジェクトがプロセスの範囲外で一意のIDを持っている場合、それはIDを含む同一の属性を持つ新しいレコードを作成することと同等であるため、クローン可能にしたくありません。データベースキーのような関連する属性。これはほとんど正しいことではありません。同時に、安定性やその他の理由でシステムが複数のプロセスに分割されている可能性があるため、1つのプロセスがデータベースと通信してこれらの「ENTITY」オブジェクトを生成する場合があります(詳細については、EricEvansによる「ドメイン駆動設計」を参照してください)。データベースのアプリケーションでオブジェクトIDの一貫性を維持するための情報)が、別のプロセスがこれらのオブジェクトを使用してビジネスロジック操作を実行する場合があります。

于 2010-01-24T18:47:23.730 に答える
2

シリアライズ可能インターフェイスとクローン可能インターフェイスは、まったく異なる目的で使用する必要があると思います。また、複雑なクラスがある場合、それぞれを実装するのはそれほど簡単ではありません。したがって、一般的には目的によって異なります。

于 2010-01-24T18:50:18.173 に答える
1

さて、あなたはシリアル化メカニズムがオブジェクトを間接的に「クローン」する1つの方法であると言っています。もちろん、それはその主要な機能ではありません。これは通常、プログラムがネットワークを介してオブジェクトを送信したり、保存して後で読み取ったりするために使用されます。オブジェクトがこのように使用され、Serializableを実装することを期待できますが、コードがオブジェクトをローカルに複製することや、Cloneableを実装することは期待できません。

コードがシリアル化によってこれを回避しているという事実は、コードが作成者が意図していなかった方法でオブジェクトを使用していることを示唆しています。これは、作成者または呼び出し元の「障害」のいずれかである可能性がありますが、一般的にシリアル化可能であり、クローン可能は一緒に行きます。

これとは別に、clone()が正しく実装するのが難しいほど「壊れている」かどうかはわかりません。コピーコンストラクターは、より自然に使用でき、正しいIMHOを取得できます。

于 2010-01-24T18:48:25.620 に答える
1

Serializable の最大の問題の 1 つは、それらを簡単に不変にできないことです。シリアライゼーションでは、ここで妥協する必要があります。

オブジェクト グラフの下にコピー コンストラクターを作成したくなります。もう少し面倒な作業です。

于 2010-01-24T23:13:21.707 に答える
0

シリアル化には多くの落とし穴があるので、それは一種の危険なことだと思います(それらのほとんどはありそうもないですが、サードパーティライブラリでオブジェクトをシリアル化する場合はテストしたいと思います)。一般的な問題ではない可能性がありますが、クローン操作の一部である可能性のある状態の一部として揮発性変数を持つオブジェクトを持つことができます(これは優れた設計ではなく、可能であるというだけです)。フィールドは、シリアル化/逆シリアル化プロセスでコピーされません。頭に浮かぶもう1つの問題は、列挙型、定数、および逆シリアル化中にそれらを処理しない場合にそのようなものの複数のコピーを取得する可能性です。

繰り返しになりますが、エッジケースですが、注意したいことがあります。

于 2010-01-24T18:50:33.553 に答える
0

別のケースを考えました-それが列挙型の場合。

または、より一般的には、クラスがreadResolve. これは、返されるオブジェクトがreadObjectストリームから読み取られたものと同じではないことを意味します。したがって、ストリームに最初に書き込まれたオブジェクトのコピーであるとは限りません。

于 2011-10-22T18:02:57.043 に答える