Javaでは、ディープオブジェクトコピー機能を実装するのは少し難しいです。元のオブジェクトと複製されたオブジェクトが参照を共有しないようにするためにどのような手順を踏みますか。
安全な方法は、オブジェクトをシリアライズしてからデシリアライズすることです。これはすべてが真新しい参照であることを保証します。
これが記事です これを効率的に行う方法について。
警告:新しいインスタンスが作成されないように、クラスが直列化をオーバーライドすることは可能です。シングルトン用。あなたのクラスが直列化可能ではない場合にもこれはもちろん動作しません。
少数の人々がObject.clone()
の使用またはオーバーライドについて述べています。しないでください。 Object.clone()
にはいくつかの大きな問題があり、その使用はほとんどの場合推奨されません。完全な答えについては、Joshua Blochによる " Effective Java "のItem 11を見てください。私はあなたが安全にObject.clone()
をプリミティブ型配列に使うことができると思いますが、それとは別にあなたはクローンを適切に使いそして上書きすることについて慎重になる必要があります。
シリアライゼーション(XMLまたはその他)に依存している方式は非常に簡単です。
ここには簡単な答えはありません。オブジェクトをディープコピーする場合は、オブジェクトグラフをトラバースして、オブジェクトのコピーコンストラクタまたは静的ファクトリメソッドを介して各子オブジェクトを明示的にコピーする必要があります。不変物(例:String
s)はコピーする必要はありません。余談ですが、この理由から不変性を優先する必要があります。
ファイルを作成せずにシリアライゼーションでディープコピーを作成できます。
ディープコピーしたいオブジェクトはimplement serializable
にする必要があります。クラスが最終的なものではない場合や変更できない場合は、クラスを拡張して直列化可能を実装します。
クラスをバイトストリームに変換します。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
バイトストリームからクラスを復元します。
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
Apache Commons Langでは org.Apache.commons.lang3.SerializationUtils.clone(T)
を使用してシリアル化ベースのディープクローンを作成できますが、注意が必要です - パフォーマンスは非常に悪いです。
一般に、複製が必要なオブジェクトグラフ内のオブジェクトのクラスごとに独自のクローンメソッドを作成することをお勧めします。
ディープコピーを実装する1つの方法は、関連する各クラスにコピーコンストラクタを追加することです。コピーコンストラクタは 'this'のインスタンスを単一の引数として取り、そこからすべての値をコピーします。かなりの作業がありますが、かなり簡単で安全です。
編集:フィールドを読むためにアクセサメソッドを使う必要はないことに注意してください。ソースインスタンスは常にコピーコンストラクタを持つインスタンスと同じ型であるため、すべてのフィールドに直接アクセスできます。明らかですが見落とされるかもしれません。
例:
public class Order {
private long number;
public Order() {
}
/**
* Copy constructor
*/
public Order(Order source) {
number = source.number;
}
}
public class Customer {
private String name;
private List<Order> orders = new ArrayList<Order>();
public Customer() {
}
/**
* Copy constructor
*/
public Customer(Customer source) {
name = source.name;
for (Order sourceOrder : source.orders) {
orders.add(new Order(sourceOrder));
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
編集:コピーコンストラクタを使用するときあなたがコピーしているオブジェクトの実行時の型を知る必要があることに注意してください。上記の方法では、混在リストを簡単にコピーすることはできません(いくつかのリフレクションコードでそれを実行できるかもしれません)。
Apache commonsはオブジェクトをディープクローンするための迅速な方法を提供します。
My_Object object2= org.Apache.commons.lang.SerializationUtils.clone(object1);
あなたは シンプルなAPIを持ち、リフレクションを使って比較的速いクローン作成を実行するライブラリ を使うことができます(直列化メソッドより速いはずです)。
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
XStreamはそのような場合に本当に便利です。これはクローニングを行うための簡単なコードです。
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
非常に簡単で簡単な方法の1つは、Jackson JSONを使用して複雑なJavaオブジェクトをJSONにシリアライズして読み戻すことです。
Spring Frameworkユーザー向け。クラスorg.springframework.util.SerializationUtils
を使う:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
XStream( http://x-stream.github.io/ )を使用してください。どのプロパティを無視できるかをアノテーションを使って制御することも、XStreamクラスにプロパティ名を明示的に指定することもできます。さらにあなたはクローン可能なインターフェースを実装する必要はありません。
ディープコピーは、各クラスの同意がある場合にのみ実行できます。クラス階層を制御できる場合は、クローンインタフェースを実装してCloneメソッドを実装できます。そうでなければ、オブジェクトが非データリソース(例えばデータベース接続)も共有している可能性があるので、ディープコピーを行うことは安全に行うことは不可能である。しかし一般的にディープコピーはJava環境では悪い習慣であると考えられており、適切な設計方法によって回避されるべきです。
複雑なオブジェクトの場合やパフォーマンスが重要でない場合は、 gson のようにjsonライブラリを使用してオブジェクトをjsonテキストにシリアル化し、次にテキストを逆シリアル化して新しいオブジェクトを取得します。
リフレクションに基づくgsonはほとんどの場合に動作しますが、ただしtransient
フィールドはコピーされず、オブジェクトは循環参照でStackOverflowError
となります。
public static <T> T copy(T anObject, Class<T> classInfo) {
Gson gson = new GsonBuilder().create();
String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);
return newObject;
}
public static void main(String[] args) {
String originalObject = "hello";
String copiedObject = copy(originalObject, String.class);
}
import com.thoughtworks.xstream.XStream;
public class deepCopy {
private static XStream xstream = new XStream();
//serialize with Xstream them deserialize ...
public static Object deepCopy(Object obj){
return xstream.fromXML(xstream.toXML(obj));
}
}
1)
public static Object deepClone(Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
2)
// (1) create a MyPerson object named Al
MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
MyPerson al = new MyPerson("Al", "Arun", address);
// (2) make a deep clone of Al
MyPerson neighbor = (MyPerson)deepClone(al);
ここであなたのMyPersonとMyAddressクラスはシリアライズ可能なインターフェースを実装しなければなりません
BeanUtils ビーンのディープクローニングには本当にいい仕事をします。
BeanUtils.cloneBean(obj);