it-swarm-tr.com

Java'da bir nesnenin derin bir kopyasını nasıl yaparsınız?

Java öğesinde, derin bir nesne kopyalama işlevi uygulamak biraz zor. Orijinal nesnenin ve klonlanmış olanın referans paylaşmamasını sağlamak için attığınız adımlar?

277
Andrei Savu

Güvenli bir yol nesneyi serileştirmek, ardından seri hale getirmektir. Bu, her şeyin yepyeni bir referans olmasını sağlar.

İşte bir makale bunun verimli bir şekilde nasıl yapılacağı hakkında.

Uyarılar: Sınıfların, yeni örneklerin yaratılmadığı ör. singletons için. Ayrıca, bu, eğer sınıflarınız seri hale getirilemezse, elbette çalışmaz.

161
Jason Cohen

Bir kaç kişi Object.clone() adresini kullanarak ya da geçersiz kıldığından bahsetti. Yapma Object.clone() bazı önemli problemlere sahiptir ve çoğu durumda kullanımı önerilmez. Tam bir cevap için lütfen Madde 11'e, Joshua Bloch'un " Etkili Java " kısmından bakın. İlkel tür dizilerde Object.clone() işlevini güvenle kullanabileceğinizi düşünüyorum, ancak bunun dışında klonu uygun şekilde kullanma ve geçersiz kılma konusunda makul davranmanız gerekir.

Serileştirmeye dayanan programlar (XML veya başka türlü) çok ağırdır.

Burada kolay bir cevap yok. Bir nesneyi derinlemesine kopyalamak istiyorsanız, nesne grafiğinin üzerinden geçmeniz ve her alt nesneyi açıkça nesnenin kopya kurucusu ya da alt nesnesini derin olarak kopyalayan statik bir fabrika yöntemi aracılığıyla kopyalamanız gerekir. Değiştirilemezlerin (örneğin Strings) kopyalanması gerekmez. Bir kenara, bu nedenle değişmezliği tercih etmelisiniz.

67
Julien Chastang

Dosya oluşturmadan seri hale getirme ile derin bir kopyalama yapabilirsiniz.

Derin kopyalamak istediğiniz nesnenin implement serializable. Sınıf kesin değilse veya değiştirilemiyorsa, sınıfı genişletin ve seri hale getirilebilir uygulayın.

Sınıfınızı bir bayt akışına dönüştürün:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

Sınıfınızı bir bayt akışından geri yükleyin:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
54
Thargor

Serileştirme tabanlı bir derin klonu Apache Commons Lang'ta org.Apache.commons.lang3.SerializationUtils.clone(T) kullanarak yapabilirsiniz, ancak dikkatli olun - performans berbat.

Genel olarak, klonlama gerektiren nesne grafiğindeki bir nesnenin her sınıfı için kendi klonlama yöntemlerinizi yazmak en iyi yöntemdir.

37
user8690

Derin kopya uygulamanın bir yolu, ilişkili her sınıfa kopya kurucuları eklemektir. Bir copy yapıcısı 'this'in bir örneğini tekil argümanı olarak alır ve ondan tüm değerleri kopyalar. Biraz iş, ama oldukça basit ve güvenli.

EDIT: alanları okumak için erişim yöntemleri kullanmanıza gerek olmadığını unutmayın. Tüm alanlara doğrudan erişebilirsiniz, çünkü kaynak örnek daima kopya kurucu ile aynıdır. Açıktır ama göz ardı edilebilir.

Örnek:

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;
    }
}

Düzenleme: Kopya oluşturucuları kullanırken kopyaladığınız nesnenin çalışma zamanını bilmeniz gerektiğini unutmayın. Yukarıdaki yaklaşımla kolayca karışık bir listeyi kopyalayamazsınız (bazı yansıma kodlarıyla yapabilirsiniz).

23
Adriaan Koster

Apache Commons, bir nesneyi derin klonlamak için hızlı bir yol sunar.

My_Object object2= org.Apache.commons.lang.SerializationUtils.clone(object1);
19
TheByeByeMan

Basit bir API'ye sahip olan ve yansıma ile nispeten hızlı klonlama yapan seri hale getirme yöntemlerinden daha hızlı olmalıdır yapabilirsiniz.

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
18
CorayThan

XStream bu gibi durumlarda gerçekten kullanışlıdır. İşte klonlama yapmak için basit bir kod

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
11
sankara

Çok kolay ve basit bir yaklaşım, karmaşık Java Nesnesini JSON'a seri hale getirmek ve onu okumak için Jackson JSON kullanmaktır.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

Spring Framework kullanıcıları için. org.springframework.util.SerializationUtils sınıfını kullanarak:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
8
Igor Rybak

XStream ( http://x-stream.github.io/ ) kullanın. Ek açıklamalarla görmezden gelebileceğiniz veya özellik adını açıkça XStream sınıfına belirten özellikleri bile kontrol edebilirsiniz. Üstelik klonlanabilir bir arayüz uygulamanıza gerek yoktur.

8
Adi

Karmaşık nesneler için ve performans önemli olmadığında, json metnini seri hale getirmek için gson gibi bir json kütüphanesi kullanır, sonra yeni nesneyi almak için metni seri hale getirir.

yansıtmaya dayalı gson, transient alanları kopyalanmayacak ve StackOverflowError nedeni ile dairesel referansa sahip nesneler hariç, çoğu durumda işe yarayacaktır.

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);
}
7
tiboo

Derin kopyalama yalnızca her sınıfın rızasıyla yapılabilir. Sınıf hiyerarşisi üzerinde kontrolünüz varsa, klonlanabilir arayüzü uygulayabilir ve Klonlama yöntemini uygulayabilirsiniz. Aksi halde, derin bir kopya yapmak güvenli bir şekilde imkansızdır, çünkü nesne veri dışı kaynakları da paylaşıyor olabilir (örneğin, veritabanı bağlantıları). Bununla birlikte, genel olarak, derin kopyalamanın Java ortamında kötü bir uygulama olduğu kabul edilir ve uygun tasarım uygulamaları ile kaçınılması gerekir.

7
Orion Adrian
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));
    }
}
6
user946968

Java nesnelerini klonlamak için Dozer kullandım ve bu harika, Kryo kütüphane başka bir harika alternatif.

4
supernova

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);

Burada MyPerson ve MyAddress sınıfınız serilazable arabirim uygulamalıdır

2
Arun

BeanUtils çekirdekleri klonlamak için gerçekten iyi bir iş çıkarıyor.

BeanUtils.cloneBean(obj);
1
Alfergon