Pergunta

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

Foi útil?

Solução

Uma maneira segura é serializar o objeto e depois desserializá-lo.Isso garante que tudo seja uma nova referência.

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

Ressalvas:É possível que as classes substituam a serialização de modo que novas instâncias sejam não criado, por ex.para solteiros.Além disso, é claro que isso não funciona se suas classes não forem serializáveis.

Outras dicas

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.Consulte o item 11, de "Java eficaz" por Joshua Bloch para uma resposta completa.Eu acredito que você pode usar com segurança Object.clone() em matrizes de tipo primitivo, mas, fora isso, você precisa ser criterioso ao usar e substituir corretamente o clone.

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

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

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

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

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 prática recomendada escrever seus próprios métodos de clonagem para cada classe de um objeto no gráfico de objetos que precisa de clonagem.

Uma maneira de implementar cópia profunda é adicionar construtores de cópia a cada classe associada.Um construtor de cópia pega uma instância de 'this' como seu único argumento e copia todos os valores dela.Bastante trabalhoso, mas bastante simples e seguro.

EDITAR:observe que você não precisa usar métodos acessadores 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 esquecido.

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

Editar: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 (talvez você consiga fazer isso com algum código de reflexão).

O Apache Commons oferece uma maneira rápida de clonar profundamente um objeto.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);

Você pode usar 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

XStream é realmente útil nesses casos.Aqui está um código simples para fazer clonagem

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

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

Uma abordagem muito fácil e simples é usar Jackson JSON para serializar objetos Java complexos em JSON e lê-los de volta.

http://wiki.fasterxml.com/JacksonInFiveMinutes

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

Para Estrutura Primavera Usuários.Usando classe org.springframework.util.SerializationUtils:

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

A cópia profunda só pode ser feita com o consentimento de cada turma.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, porém, a cópia profunda é considerada uma má prática no ambiente Java e deve ser evitada através das práticas de design apropriadas.

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

Para objetos complicados e quando o desempenho não é significativo eu uso uma biblioteca json, como filhopara serializar o objeto em texto json e desserializar o texto para obter o novo objeto.

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

eu usei Escavadora para clonar objetos java e é ótimo nisso, Kryo biblioteca é outra ótima alternativa.

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 serilazável

BeanUtils faz um ótimo trabalho de clonagem profunda de beans.

BeanUtils.cloneBean(obj);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top