Jak skopiować obiekt w Javie?

Rozważ poniższy kod:

DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'

Chcę więc skopiować dum do dumtwo I zmienić dum bez wpływu na dumtwo. Ale powyższy kod tego nie robi. Kiedy zmieniam coś w dum, ta sama zmiana dzieje się również w dumtwo.

Wydaje mi się, że Kiedy mówię dumtwo = dum, Java kopiuje tylko odniesienia . Czy jest jakiś sposób na utworzenie nowej kopii dum i przypisanie jej do dumtwo?

Author: Mooncrater, 2009-05-15

21 answers

Utwórz konstruktor kopii:

class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // you can access  
  }
}

Każdy obiekt ma również metodę klonowania, która może być użyta do skopiowania obiektu, ale jej nie używaj. Zbyt łatwo jest utworzyć klasę i wykonać niewłaściwą metodę klonowania. Jeśli masz zamiar to zrobić, przeczytaj przynajmniej co Joshua Bloch ma do powiedzenia na ten temat w skuteczna Java.

 537
Author: egaga,
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
2009-05-15 14:47:40

Basic: kopiowanie obiektów w Javie.

Przyjmijmy obiekt - obj1, który zawiera dwa obiekty, containedObj1 i containedObj2.
Tutaj wpisz opis obrazka

Płytkie kopiowanie:
shallow copying tworzy nową instance tej samej klasy i kopiuje wszystkie pola do nowej instancji i zwraca je. klasa obiektu zapewnia metodę clone i zapewnia wsparcie dla płytki kopiuję.
Tutaj wpisz opis obrazka

Głębokie kopiowanie:
Głęboka Kopia występuje, gdy obiekt jest kopiowany wraz z obiektami, do których się odnosi . Poniższy obrazek pokazuje obj1 po wykonaniu na nim głębokiej kopii. nie tylko obj1 zostały skopiowane , ale także obiekty w nim zawarte. Możemy użyć Java Object Serialization do zrobienia głębokiej kopii. Niestety, takie podejście ma również pewne problemy (szczegółowe przykłady ).
Tutaj wpisz opis obrazka

Możliwe problemy:
clone jest trudne do prawidłowego wdrożenia.
Lepiej używać defensywnego kopiowania, konstruktory kopiujące(jako odpowiedź @egaga) lub statyczne metody fabryczne .

  1. jeśli masz obiekt, o którym wiesz, że ma publiczną metodę clone(), ale nie znasz typu obiektu w czasie kompilacji, to masz problem. Java posiada interfejs o nazwie Cloneable. W praktyce powinniśmy wdrożyć to interfejs, jeśli chcemy stworzyć obiekt Cloneable. Object.clone jest chronione , więc musimy nadpisać, aby był dostępny.
  2. inny problem pojawia się, gdy próbujemy głębokiego kopiowania złożonego obiektu. Załóżmy, że metoda clone() wszystkich zmiennych obiektu członkowskiego również wykonuje głęboką kopię, jest to zbyt ryzykowne z założenia. Musisz kontrolować kod we wszystkich klasach.

Na przykład org.Apacz.commons.lang.SerializationUtils będzie miał metodę głębokiego klonowania przy użyciu serializacji (źródło ). Jeśli musimy sklonować Bean, istnieje kilka metod użytkowych w org.Apacz.commons.beanutils (źródło ).

  • cloneBean Sklonuje bean na podstawie dostępnych właściwości getters i setters, nawet jeśli sama klasa bean nie implementuje Cloneable.
  • copyProperties skopiuje wartości właściwości z oryginału do miejsce przeznaczenia dla wszystkich przypadków, w których nazwy nieruchomości są takie same.
 359
Author: Chandra Sekhar,
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-05-02 04:50:10

W OPAKOWANIU import org.apache.commons.lang.SerializationUtils; znajduje się metoda:

SerializationUtils.clone(Object);

Przykład:

this.myObjectCloned = SerializationUtils.clone(this.object);
 94
Author: pacheco,
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-05-02 20:14:54

Po prostu postępuj jak poniżej:

public class Deletable implements Cloneable{

    private String str;
    public Deletable(){
    }
    public void setStr(String str){
        this.str = str;
    }
    public void display(){
        System.out.println("The String is "+str);
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

I gdziekolwiek chcesz uzyskać inny obiekt, wykonaj proste klonowanie. e. g:

Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone(); // this line will return you an independent
                                 // object, the changes made to this object will
                                 // not be reflected to other object
 93
Author: Bhasker Tiwari,
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
2011-10-20 15:46:05

Dlaczego nie ma odpowiedzi na używanie API Reflection?

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                field.set(clone, field.get(obj));
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }
To naprawdę proste.

EDIT: Include child object via recursion

private static Object cloneObject(Object obj){
        try{
            Object clone = obj.getClass().newInstance();
            for (Field field : obj.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
                    continue;
                }
                if(field.getType().isPrimitive() || field.getType().equals(String.class)
                        || field.getType().getSuperclass().equals(Number.class)
                        || field.getType().equals(Boolean.class)){
                    field.set(clone, field.get(obj));
                }else{
                    Object childObj = field.get(obj);
                    if(childObj == obj){
                        field.set(clone, clone);
                    }else{
                        field.set(clone, cloneObject(field.get(obj)));
                    }
                }
            }
            return clone;
        }catch(Exception e){
            return null;
        }
    }
 34
Author: WillingLearner,
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-08-29 09:49:50

Używam Biblioteki JSON Google, aby serializować go, a następnie utworzyć nową instancję serializowanego obiektu. Robi głębokie kopiowanie z kilkoma ograniczeniami:

  • Nie może być żadnych odwołań rekurencyjnych

  • Nie będzie kopiować tablic różnych typów

  • Tablice i listy powinny być wpisywane lub nie znajdzie klasy do utworzenia instancji

  • Może być konieczne zamknięcie łańcuchów w klasie, którą deklarujesz

Używam również tej klasy do Zapisz preferencje użytkownika, okna i inne rzeczy, które mają być przeładowane w czasie wykonywania. Jest bardzo łatwy w użyciu i skuteczny.

import com.google.gson.*;

public class SerialUtils {

//___________________________________________________________________________________

public static String serializeObject(Object o) {
    Gson gson = new Gson();
    String serializedObject = gson.toJson(o);
    return serializedObject;
}
//___________________________________________________________________________________

public static Object unserializeObject(String s, Object o){
    Gson gson = new Gson();
    Object object = gson.fromJson(s, o.getClass());
    return object;
}
       //___________________________________________________________________________________
public static Object cloneObject(Object o){
    String s = serializeObject(o);
    Object object = unserializeObject(s,o);
    return object;
}
}
 22
Author: Peter,
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-07-09 20:14:24

Tak, tylko odwołujesz się do obiektu. Możesz sklonować obiekt, jeśli implementuje on Cloneable.

Sprawdź ten artykuł na wiki o kopiowaniu obiektów.

Zobacz tutaj: kopiowanie obiektów

 21
Author: Chrisb,
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-28 09:40:26

Tak. Musisz głęboko skopiować swój obiekt.

 12
Author: bruno conde,
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
2009-05-15 14:35:02

Dodaj Cloneable i poniższy kod do swojej klasy

public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

Użyj tego clonedObject = (YourClass) yourClassObject.clone();

 12
Author: Teja Maridu,
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-07-31 15:00:04

Oto przyzwoite Wyjaśnienie clone() jeśli będziesz tego potrzebował...

Tutaj: clone (metoda Java)

 9
Author: Jon Bringhurst,
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-28 09:41:02

To też działa. Model zakładający

class UserAccount{
   public int id;
   public String name;
}

Pierwszy dodaj compile 'com.google.code.gson:gson:2.8.1' do Twojej aplikacji>gradle & sync. Then

Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);

Można wykluczyć użycie pola za pomocą słowa kluczowego transient po modyfikatorze dostępu.

Uwaga: to jest zła praktyka. Nie zaleca się również używania Cloneable lub JavaSerialization jest powolny i zepsuty. Napisz Konstruktor kopiujący dla najlepszej wydajności ref .

Coś jak

class UserAccount{
        public int id;
        public String name;
        //empty constructor
        public UserAccount(){}
        //parameterize constructor
        public UserAccount(int id, String name) {
            this.id = id;
            this.name = name;
        }

        //copy constructor
        public UserAccount(UserAccount in){
            this(in.id,in.name);
        }
    }

Statystyki testu 90000 iteracji:
Linia UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); zajmuje 808ms

Linia UserAccount clone = new UserAccount(aO); zajmuje mniej niż 1ms

Wniosek: użyj gson, jeśli twój szef jest szalony i wolisz szybkość. Użyj drugiego konstruktora kopiującego, jeśli wolisz jakość.

Możesz również użyć kodu konstruktora kopiującego wtyczki generatora W Android Studio.

 8
Author: Qamar,
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-09-23 06:24:15

Aby to zrobić, musisz sklonować obiekt w jakiś sposób. Chociaż Java ma mechanizm klonowania, nie używaj go, jeśli nie musisz. Utwórz metodę kopiowania, która wykonuje Kopiowanie za Ciebie, a następnie wykonaj:

dumtwo = dum.copy();

Tutaj jest kilka porad na temat różnych technik wykonania kopii.

 7
Author: Yishai,
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-05-23 12:26:42

Głębokie klonowanie jest twoją odpowiedzią, która wymaga implementacji interfejsu Cloneable i nadpisania metody clone().

public class DummyBean implements Cloneable {

   private String dummy;

   public void setDummy(String dummy) {
      this.dummy = dummy;
   }

   public String getDummy() {
      return dummy;
   }

   @Override
   public Object clone() throws CloneNotSupportedException {
      DummyBean cloned = (DummyBean)super.clone();
      cloned.setDummy(cloned.getDummy());
      // the above is applicable in case of primitive member types, 
      // however, in case of non primitive types
      // cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone());
      return cloned;
   }
}

Nazwiesz to tak DummyBean dumtwo = dum.clone();

 7
Author: abbas,
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-30 19:51:59

Użyj narzędzia do głębokiego klonowania:

SomeObjectType copy = new Cloner().deepClone(someObject);

To głęboko skopiuje dowolny obiekt java, sprawdź to na https://github.com/kostaskougios/cloning

 6
Author: Cojones,
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-12-13 14:43:22

INNE niż jawne kopiowanie, inne podejście polega na uczynieniu obiektu niezmiennym (brak set lub innych metod mutatora). W ten sposób pytanie nigdy nie powstaje. Niezmienność staje się trudniejsza w przypadku większych obiektów, ale ta druga strona jest taka, że popycha cię w kierunku podziału na spójne małe obiekty i kompozyty.

 5
Author: Tom Hawtin - tackline,
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
2009-05-15 14:43:35
class DB {
  private String dummy;

  public DB(DB one) {
    this.dummy = one.dummy; 
  }
}
 3
Author: Mahdi Abdi,
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-03-02 13:35:20

Podaj obiekt, który chcesz skopiować i pobierz obiekt, który chcesz skopiować,

private Object copyObject(Object objSource) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(objSource);
            oos.flush();
            oos.close();
            bos.close();
            byte[] byteData = bos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
            try {
                objDest = new ObjectInputStream(bais).readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return objDest;

    }

Teraz przeanalizuj obiekt, który jest najbardziej obiektowy.

Happy Coding

 2
Author: A-Droid Tech,
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-12-07 13:25:22

Możesz spróbować zaimplementować Cloneable i użyć metody clone(); jednak jeśli używasz metody klonowania, powinieneś - standardowo-zawsze nadpisać Object'S public Object clone() metoda.

 1
Author: ,
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
2011-10-20 15:47:35

Możesz kopiować automatycznie za pomocą XStream, z http://x-stream.github.io/:

XStream jest prostą biblioteką do serializacji obiektów do XML i z powrotem jeszcze raz.

Dodaj go do swojego projektu (jeśli używasz Mavena)

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.3.1</version>                
</dependency>

Then

DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));

Z tym masz kopię bez konieczności implementacji jakiegokolwiek interfejsu klonowania.

 1
Author: Jaime Hablutzel,
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-08-11 18:35:31

Jeśli możesz dodać adnotację do pliku źródłowego, można użyć procesora adnotacji lub generatora kodu, takiego jak Ten.

import net.zerobuilder.BeanBuilder

@BeanBuilder
public class DummyBean { 
  // bean stuff
}

Zostanie wygenerowana klasa DummyBeanBuilders, która ma statyczną metodę dummyBeanUpdater do tworzenia płytkich kopii, tak samo jak zrobiłbyś to ręcznie.

DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();
 0
Author: Lars Bohl,
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-02-14 05:45:49
public class MyClass implements Cloneable {

private boolean myField= false;
// and other fields or objects

public MyClass (){}

@Override
public MyClass clone() throws CloneNotSupportedException {
   try
   {
       MyClass clonedMyClass = (MyClass)super.clone();
       // if you have custom object, then you need create a new one in here
       return clonedMyClass ;
   } catch (CloneNotSupportedException e) {
       e.printStackTrace();
       return new MyClass();
   }

  }
}

I w kodzie:

MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();
 0
Author: Amir Hossein Ghasemi,
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-08-10 13:26:48