Pregunta ¿Cómo copio un objeto en Java?


Considera el siguiente código:

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'

Entonces, quiero copiar el dum a dumtwo y cambio dum sin afectar el dumtwo. Pero el código de arriba no está haciendo eso. Cuando cambio algo en dum, el mismo cambio está sucediendo en dumtwo además.

Supongo, cuando digo dumtwo = dum, Java copia el solo referencia. Entonces, ¿hay alguna manera de crear una copia nueva de dum y asignarlo a dumtwo?


649
2018-05-15 14:30


origen


Respuestas:


Crea un constructor de copia:

class DummyBean {
  private String dummy;

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

Cada objeto también tiene un método de clonación que puede usarse para copiar el objeto, pero no lo usa. Es demasiado fácil crear una clase y hacer un método de clonación inadecuado. Si va a hacer eso, lea al menos lo que Joshua Bloch tiene que decir al respecto en Java efectivo.


525
2018-05-15 14:35



BASIC: Copia de objetos en Java.

Supongamos un objeto- obj1, que contiene dos objetos, containedObj1 y containedObj2.
enter image description here

copia superficial:
copia poco profunda crea una nueva instance de la misma clase y copia todos los campos a la nueva instancia y la devuelve. Clase de objeto provee un clone método y proporciona soporte para la copia superficial.
enter image description here

Copia profunda:
Una copia profunda ocurre cuando un objeto se copia junto con los objetos a los que se refiere. Debajo de la imagen muestra obj1 después de una copia profunda se ha realizado en él. No solo tiene obj1 sido copiado, pero los objetos que contiene también han sido copiados. Nosotros podemos usar Java Object Serialization para hacer una copia profunda Desafortunadamente, este enfoque también tiene algunos problemas (ejemplos detallados)
enter image description here

Posibles problemas:
clone es complicado de implementar correctamente.
Es mejor usar Copia defensiva, copia constructores(como la respuesta @egaga) o métodos estáticos de fábrica.

  1. Si tiene un objeto, que sabe que tiene un público clone() método, pero no se sabe el tipo de objeto en tiempo de compilación, entonces tiene un problema. Java tiene una interfaz llamada Cloneable. En la práctica, debemos implementar esta interfaz si queremos hacer un objeto Cloneable. Object.clone es protegido, entonces debemos anular con un método público para que sea accesible.
  2. Otro problema surge cuando lo intentamos copia profunda de un objeto complejo. Suponer que el clone() El método de todas las variables de objeto miembro también hace una copia profunda, esto es demasiado arriesgado para una suposición. Debes controlar el código en todas las clases.

Por ejemplo org.apache.commons.lang.SerializationUtils tendrá un método para Deep clone usando la serialización (Fuente) Si necesitamos clonar Bean, hay algunos métodos de utilidad en org.apache.commons.beanutils (Fuente)

  • cloneBean clonará un bean basado en los getters y setters de propiedad disponibles, incluso si la clase de bean en sí misma no implementa Cloneable.
  • copyPropertiesCopiará los valores de las propiedades del bean de origen al bean de destino para todos los casos en que los nombres de las propiedades sean los mismos.

352
2018-03-23 05:47



Simplemente siga lo siguiente:

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

y donde quiera obtener otro objeto, realice clonación simple. p.ej:

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

92
2018-05-17 09:27



En el paquete import org.apache.commons.lang.SerializationUtils; hay un método:

SerializationUtils.clone(Object);

Ejemplo:

this.myObjectCloned = SerializationUtils.clone(this.object);

91
2018-05-02 19:45



¿Por qué no hay respuesta para usar Reflection API?

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

Es realmente simple.

EDITAR: incluye un objeto secundario a través de la recursión

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
2017-08-16 09:27



Utilizo la biblioteca JSON de Google para serializarlo y luego crear una nueva instancia del objeto serializado. Hace una copia profunda con algunas restricciones:

  • no puede haber referencias recursivas

  • no copiará matrices de tipos dispares

  • matrices y listas deben escribirse o no encontrará la clase para instanciar

  • es posible que necesite encapsular cadenas en una clase que usted declare

También utilizo esta clase para guardar las preferencias del usuario, las ventanas y lo que no se vuelva a cargar en el tiempo de ejecución. Es muy fácil de usar y efectivo.

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
2017-07-09 20:14



Sí, solo estás haciendo una referencia al objeto. Puedes clonar el objeto si implementa Cloneable.

Echa un vistazo a este artículo de wiki sobre copiar objetos.

Consulte aquí: copia de objetos


21
2018-05-15 14:36



Sí. Necesitas Copia profunda tu objeto


12
2018-05-15 14:35



Añadir Cloneable y debajo del código a su clase

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

Utilizar esta clonedObject = (YourClass) yourClassObject.clone();


12
2017-07-31 15:00



Aquí hay una explicación decente de clone() si terminas necesitándolo ...

Aquí: clon (método de Java)


9
2018-05-15 14:35



Esto también funciona Asumiendo el modelo

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

Primero agrega compile 'com.google.code.gson:gson:2.8.1' a tu aplicación> gradle & sync. Entonces

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

Puede excluir el uso de un campo mediante el uso de transient palabra clave después del modificador de acceso.

Nota: Esta es una mala práctica. Tampoco recomendamos usar Cloneable o JavaSerialization Es lento y roto. Escribir el constructor de copia para un mejor rendimiento árbitro.

Algo como

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

Estadísticas de prueba de 90000 iteraciones:
Línea UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class); toma 808ms

Línea UserAccount clone = new UserAccount(aO); toma menos de 1ms

Conclusión: Use gson si su jefe está loco y prefiere la velocidad. Use el constructor de segunda copia si prefiere la calidad.

También puedes usar el código de constructor de copia plugin generador en Android Studio.


8
2018-01-26 08:50