Jak zrobić głęboką kopię obiektu w Javie?

W Javie jest nieco trudno zaimplementować funkcję głębokiego kopiowania obiektów. Jakie kroki należy podjąć, aby upewnić się, że oryginalny obiekt i sklonowany nie mają żadnego odniesienia?

Author: Lazer, 2008-09-15

17 answers

Bezpiecznym sposobem jest serializacja obiektu, a następnie deserializacja. Zapewnia to, że wszystko jest zupełnie nowym punktem odniesienia.

Oto Artykuł o tym, jak to zrobić skutecznie.

Zastrzeżenia: możliwe jest, aby klasy nadpisywały serializację tak, że nowe instancje są tworzone , a nie, np. dla singletonów. Również to oczywiście nie działa, jeśli Twoje klasy nie są Serializowalne.

 151
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 15:42:23

Kilka osób wspomniało o używaniu lub nadpisywaniu Object.clone(). Nie rób tego. Object.clone() ma kilka poważnych problemów, a jego stosowanie jest zniechęcające w większości przypadków. Pełna odpowiedź znajduje się w punkcie 11 z "Effective Java" Joshuy Bloch. Wierzę, że można bezpiecznie używać Object.clone() Na prymitywnych tablicach typu, ale poza tym trzeba rozsądnie używać i nadpisywać klon.

Schematy, które opierają się na serializacji (XML lub w inny sposób), są kludgy.

There is no easy odpowiedz tutaj. Jeśli chcesz głęboko skopiować obiekt, musisz przejść przez wykres obiektu i skopiować każdy obiekt potomny jawnie za pomocą konstruktora kopiującego obiektu lub statycznej metody fabrycznej, która z kolei głęboko kopiuje obiekt potomny. Obiekty niezmienne (np. String s) nie muszą być kopiowane. Z tego powodu powinieneś faworyzować niezmienność.

 65
Author: Julien Chastang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 07:50:46

Możesz zrobić głęboką kopię z serializacją bez tworzenia plików.

Twój obiekt, który chcesz głęboko skopiować, będzie musiał implement serializable. Jeśli klasa nie jest ostateczna lub nie może zostać zmodyfikowana, rozszerz klasę i zaimplementuj serializable.

Konwertuj klasę na strumień bajtów:

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

Przywróć klasę ze strumienia bajtów:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
 50
Author: Thargor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-07 13:22:57

Możesz wykonać głęboki klon oparty na serializacji za pomocą org.apache.commons.lang3.SerializationUtils.clone(T) w Apache Commons Lang, ale uważaj-wydajność jest fatalna.

Ogólnie rzecz biorąc, najlepszą praktyką jest pisanie własnych metod klonowania dla każdej klasy obiektu w grafie obiektów wymagających klonowania.

 33
Author: user8690,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 14:59:52

Jednym ze sposobów implementacji głębokiej kopii jest dodanie konstruktorów kopiujących do każdej powiązanej klasy. Konstruktor kopiujący pobiera instancję 'this' jako swój pojedynczy argument i kopiuje z niego wszystkie wartości. Trochę pracy, ale całkiem proste i bezpieczne.

EDIT: zauważ, że nie musisz używać metod dostępu do odczytu pól. Możesz uzyskać dostęp do wszystkich pól bezpośrednio, ponieważ instancja źródłowa jest zawsze tego samego typu co instancja z konstruktorem kopiującym. Oczywiste, ale może być / align = "left" /

Przykład:

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

Edit: zauważ, że podczas używania konstruktorów kopiujących musisz znać typ runtime kopiowanego obiektu. Przy powyższym podejściu nie można łatwo skopiować listy mieszanej (być może uda się to zrobić za pomocą kodu refleksyjnego).

 22
Author: Adriaan Koster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 07:50:30

Apache commons oferuje szybki sposób na głębokie sklonowanie obiektu.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
 17
Author: TheByeByeMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-28 14:53:24

Możesz użyć biblioteki , która ma proste API i wykonuje stosunkowo szybkie klonowanie z odbiciem (powinno być szybsze niż metody serializacji).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
 16
Author: CorayThan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 07:51:08

XStream jest naprawdę przydatny w takich przypadkach. Oto prosty kod do klonowania

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

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
 11
Author: sankara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 08:03:08

Jednym bardzo łatwym i prostym podejściem jest użycie Jacksona JSONA do serializacji złożonego obiektu Javy do JSON i odczytania go z powrotem.

Http://wiki.fasterxml.com/JacksonInFiveMinutes

 9
Author: Ravi Chinoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-07 03:51:31

Use XStream ( http://x-stream.github.io/). możesz nawet kontrolować, które właściwości możesz ignorować poprzez adnotacje lub jawne podanie nazwy właściwości do klasy XStream. Co więcej, nie musisz implementować interfejsu klonowalnego.

 8
Author: Adi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 11:35:00

Głębokie kopiowanie może być wykonane tylko za zgodą każdej klasy. Jeśli masz kontrolę nad hierarchią klas, możesz zaimplementować interfejs klonowalny i zaimplementować metodę klonowania. W przeciwnym razie wykonanie głębokiej kopii jest niemożliwe do zrobienia bezpiecznie, ponieważ obiekt może również współdzielić zasoby inne niż dane (np. połączenia z bazą danych). Ogólnie jednak głębokie kopiowanie jest uważane za złą praktykę w środowisku Java i powinno być unikane poprzez odpowiednie praktyki projektowe.

 6
Author: Orion Adrian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 15:44:32
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
Author: Eric Leschinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-21 00:58:35

Do skomplikowanych obiektów i gdy wydajność nie jest znacząca używam biblioteki json, jak gson aby serializować obiekt do tekstu json, następnie deserializować tekst, aby uzyskać nowy obiekt.

Gson, który bazuje na odbiciu będzie działał w większości przypadków, z tą różnicą, że transient Pola nie będą kopiowane, a obiekty z kolistym odniesieniem z przyczyną 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);

}
 5
Author: tiboo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-19 10:30:33

Użyłem Dozer do klonowania obiektów Javy i jest w tym świetny, Kryo Biblioteka jest kolejną świetną alternatywą.

 4
Author: supernova,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-17 02:08:59

Dla Spring Framework użytkowników. Klasa org.springframework.util.SerializationUtils:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
 4
Author: Igor Rybak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-19 22:15:18

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

Tutaj twoja klasa MyPerson i myaddress musi zaimplementować interfejs serilazable

 2
Author: Arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-11 14:51:53

BeanUtils robi naprawdę dobrą robotę Głębokie klonowanie fasoli.

BeanUtils.cloneBean(obj);
 1
Author: Alfergon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-16 10:24:08