it-swarm-pt.tech

Como você faz uma cópia profunda de um objeto em Java?

Em Java, é um pouco difícil implementar uma função de cópia profunda de objetos. Quais etapas você toma para garantir que o objeto original e o clonado não compartilhem nenhuma referência?

273
Andrei Savu

Uma maneira segura é serializar o objeto e, em seguida, desserializar. Isso garante que tudo é uma nova referência.

Aqui está um artigo sobre como fazer isso de forma eficiente.

Advertências: É possível que as classes anulem a serialização de modo que novas instâncias sejam não criadas , por exemplo. para singletons. Isso também não funciona se suas aulas não forem serializáveis.

160
Jason Cohen

Algumas pessoas mencionaram usar ou substituir Object.clone(). Não faça isso. Object.clone() tem alguns problemas importantes, e seu uso é desencorajado na maioria dos casos. Por favor, veja o item 11, de " Effective Java " de Joshua Bloch para uma resposta completa. Acredito que você pode usar com segurança Object.clone() em matrizes de tipo primitivo, mas, além disso, é preciso ter cuidado ao usar e substituir corretamente o clone.

Os esquemas que dependem de serialização (XML ou outro) são kludgy.

Não há uma resposta fácil aqui. Se você quiser copiar um objeto em profundidade, terá que percorrer o gráfico do objeto e copiar cada objeto-filho explicitamente através do construtor de cópia do objeto ou de um método de fábrica estático que, por sua vez, copia em profundidade o objeto-filho. Imutáveis ​​(por exemplo, Strings) não precisam ser copiados. Como um aparte, você deve favorecer a imutabilidade por este motivo.

67
Julien Chastang

Você pode fazer uma cópia profunda com serialização sem criar arquivos.

Seu objeto que você deseja copiar em profundidade precisará implement serializable. Se a classe não for final ou não puder ser modificada, estenda a classe e implemente serializável.

Converta sua classe em um fluxo de bytes:

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

Restaure sua classe de um fluxo de bytes:

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

Você pode fazer um clone profundo baseado em serialização usando org.Apache.commons.lang3.SerializationUtils.clone(T) no Apache Commons Lang, mas tenha cuidado - o desempenho é péssimo.

Em geral, é uma boa prática escrever seus próprios métodos clone para cada classe de um objeto no gráfico de objeto que precisa de clonagem.

36
user8690

Uma maneira de implementar a cópia profunda é adicionar construtores de cópia a cada classe associada. Um construtor de cópia usa uma instância de 'this' como seu único argumento e copia todos os valores dele. Muito trabalho, mas bastante direto e seguro.

EDIT: note que você não precisa usar métodos de acesso para ler campos. Você pode acessar todos os campos diretamente porque a instância de origem é sempre do mesmo tipo que a instância com o construtor de cópia. Óbvio, mas pode ser ignorado.

Exemplo:

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: Observe que ao usar construtores de cópia, você precisa saber o tipo de tempo de execução do objeto que está copiando. Com a abordagem acima, você não pode copiar facilmente uma lista mista (você pode fazer isso com algum código de reflexão).

23
Adriaan Koster

O Apache commons oferece uma maneira rápida de clonar um objeto em profundidade.

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

Você pode sar uma biblioteca que possui uma API simples e executa clonagem relativamente rápida com reflexão (deve ser mais rápida que os métodos de serialização).

Cloner cloner = new Cloner();

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

XStream é realmente útil em tais instâncias. Aqui está um código simples para fazer clonagem

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

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

Uma abordagem muito fácil e simples é usar o Jackson JSON para serializar o Java Object complexo em JSON e lê-lo de volta.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
Ravi Chinoy

Para Spring Framework usuários. Usando a classe org.springframework.util.SerializationUtils:

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

Use XStream ( http://x-stream.github.io/ ). Você pode até mesmo controlar quais propriedades você pode ignorar por meio de anotações ou especificar explicitamente o nome da propriedade para a classe XStream. Além disso, você não precisa implementar a interface clonável.

8
Adi

Cópia profunda só pode ser feita com o consentimento de cada classe. Se você tiver controle sobre a hierarquia de classes, poderá implementar a interface clonável e implementar o método Clone. Caso contrário, é impossível fazer uma cópia profunda com segurança porque o objeto também pode estar compartilhando recursos que não são de dados (por exemplo, conexões de banco de dados). Em geral, no entanto, a cópia profunda é considerada uma prática ruim no ambiente Java e deve ser evitada por meio das práticas de design apropriadas.

7
Orion Adrian

Para objetos complicados e quando o desempenho não é significativo, eu uso uma biblioteca json, como gson para serializar o objeto para o texto json, então desserialize o texto para obter um novo objeto.

o gson que, baseado na reflexão, funcionará na maioria dos casos, exceto que os campos transient não serão copiados e os objetos com referência circular com a causa 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);
}
6
tiboo
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

Eu usei Dozer para clonar objetos Java e é ótimo nisso, Kryo biblioteca é outra ótima alternativa.

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

Aqui sua classe MyPerson e MyAddress deve implementar uma interface serilazable

2
Arun

BeanUtils faz um trabalho muito bom em clonagem profunda de beans.

BeanUtils.cloneBean(obj);
1
Alfergon