É um pouco difícil implementar uma função de cópia de objeto profundo. Quais etapas você toma para garantir que o objeto original e o clonado não compartilhem nenhuma referência?
Advertências: É possível que as classes substituam a serialização, de modo que novas instâncias não sejam criadas, por exemplo, para singletons. Além disso, é claro que isso não funciona se suas aulas não forem serializáveis.
Esteja ciente de que a implementação FastByteArrayOutputStream fornecida no artigo pode ser mais eficiente. Ele usa uma expansão no estilo ArrayList quando o buffer é preenchido, mas é melhor usar uma abordagem de expansão no estilo LinkedList. Em vez de criar um novo buffer 2x e copiar o buffer atual com excesso de memória, mantenha uma lista vinculada de buffers, adicionando um novo quando a corrente ficar cheia. Se você receber uma solicitação para gravar mais dados do que caberia no tamanho padrão do buffer, crie um nó de buffer que seja exatamente tão grande quanto a solicitação; os nós não precisam ter o mesmo tamanho.
A lista vinculada @BrianHarris não é mais eficiente que a matriz dinâmica. Inserção de elementos em uma matriz dinâmica é amortizado complexidade constante, durante a inserção numa lista ligada é complexidade linear
Norill Tempest
Quanto serializar e desserializar mais lentamente que a abordagem do construtor de cópias?
Woland 12/06
75
Algumas pessoas mencionaram o uso ou substituição Object.clone(). Não faça isso. Object.clone()apresenta alguns problemas importantes e seu uso é desencorajado na maioria dos casos. Consulte o Item 11, do " Java Efetivo ", de Joshua Bloch, para obter uma resposta completa. Eu acredito que você pode usar com segurança Object.clone()em matrizes de tipo primitivo, mas, além disso, você precisa ser criterioso ao usar e substituir o clone corretamente.
Os esquemas que dependem da serialização (XML ou não) são kludgy.
Não há uma resposta fácil aqui. Se você deseja copiar um objeto em profundidade, precisará 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 estático de fábrica que, por sua vez, copia profundamente o objeto filho. Imutáveis (por exemplo, Strings) não precisam ser copiados. Como um aparte, você deve favorecer a imutabilidade por esse motivo.
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.
Em geral, é uma prática recomendada escrever seus próprios métodos de clone para cada classe de um objeto no gráfico de objetos que precisam de clonagem.
Ele também está disponível emorg.apache.commons.lang.SerializationUtils
Pino
25
Uma maneira de implementar a 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 argumento único e copia todos os valores dele. Bastante trabalho, mas bem direto e seguro.
EDIT: observe que você não precisa usar métodos de acesso para ler os 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.
Editar: Observe que, ao usar construtores de cópia, você precisa conhecer 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 (poderá fazê-lo com algum código de reflexão).
Apenas interessado no caso em que o que você está copiando é uma subclasse, mas está sendo referenciado pelo pai. É possível substituir o construtor de cópia?
Bunny 'n' Bunny
Por que sua classe pai se refere à sua subclasse? Você pode dar um exemplo?
Adriaan Koster
1
classe pública Car estende Vehicle E depois se refere ao carro como veículo. originaList = new ArrayList <Veículo>; copyList = new ArrayList <Veículo>; originalList.add (novo carro ()); for (Veículo veículo: vehicleList) {copyList.add (veículo novo (veículo)); }
Pork 'n' Bunny
@AdriaanKoster: se a lista original contiver um Toyota, seu código colocará um Carna lista de destinos. A clonagem adequada geralmente exige que a classe forneça um método de fábrica virtual cujo contrato declare que ele retornará um novo objeto de sua própria classe; o próprio contratante de cópia deve protectedgarantir que ele seja usado apenas para construir objetos cujo tipo preciso corresponda ao do objeto que está sendo copiado).
Supercat
Então, se eu entendi sua sugestão corretamente, o método de fábrica chamaria o construtor de cópia privada? Como o construtor de cópias de uma subclasse garantiria que os campos da superclasse fossem inicializados? Você pode dar um exemplo?
Adriaan Koster
20
Você pode usar uma biblioteca que possui uma API simples e executa uma clonagem relativamente rápida com reflexão (deve ser mais rápida que os métodos de serialização).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
Nããão, você não precisa da sobrecarga de xml-ing do objeto.
egelev
@egeleve Você percebe que está respondendo a um comentário de 08, certo? Não uso mais o Java e provavelmente existem ferramentas melhores agora. No entanto, naquela época, serializar para um formato diferente e depois serializar de volta parecia um bom truque - era definitivamente ineficiente.
Sankara
10
Uma abordagem muito fácil e simples é usar o Jackson JSON para serializar objetos Java complexos em JSON e lê-los novamente.
Para usuários do Spring Framework . Usando classe org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Para objetos complicados e quando o desempenho não é significativo, eu uso uma biblioteca json, como gson
para serializar o objeto em texto json, depois desserializa o texto para obter um novo objeto.
O gson, que com base na reflexão, funciona na maioria dos casos, exceto que os transientcampos não serão copiados e os objetos com referência circular com causa StackOverflowError.
publicstatic<T> T copy(T anObject,Class<T> classInfo){Gson gson =newGsonBuilder().create();String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);return newObject;}publicstaticvoid main(String[] args){String originalObject ="hello";String copiedObject = copy(originalObject,String.class);}
Siga as convenções de nomenclatura Java para você e para o nosso bem.
Patrick Bergner 12/12
8
Use o XStream ( http://x-stream.github.io/ ). Você pode 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 interface clonável.
A cópia em profundidade 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 o método Clone. Caso contrário, é impossível fazer uma cópia detalhada com segurança, porque o objeto também pode estar compartilhando recursos que não são de dados (por exemplo, conexões com o banco de dados). Em geral, porém, a cópia profunda é considerada uma prática ruim no ambiente Java e deve ser evitada por meio das práticas de design apropriadas.
Respostas:
Uma maneira segura é serializar o objeto e desserializar. Isso garante que tudo seja uma nova referência.
Aqui está um artigo sobre como fazer isso com eficiência.
Advertências: É possível que as classes substituam a serialização, de modo que novas instâncias não sejam criadas, por exemplo, para singletons. Além disso, é claro que isso não funciona se suas aulas não forem serializáveis.
fonte
Algumas pessoas mencionaram o uso ou substituição
Object.clone()
. Não faça isso.Object.clone()
apresenta alguns problemas importantes e seu uso é desencorajado na maioria dos casos. Consulte o Item 11, do " Java Efetivo ", de Joshua Bloch, para obter uma resposta completa. Eu acredito que você pode usar com segurançaObject.clone()
em matrizes de tipo primitivo, mas, além disso, você precisa ser criterioso ao usar e substituir o clone corretamente.Os esquemas que dependem da serialização (XML ou não) são kludgy.
Não há uma resposta fácil aqui. Se você deseja copiar um objeto em profundidade, precisará 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 estático de fábrica que, por sua vez, copia profundamente o objeto filho. Imutáveis (por exemplo,
String
s) não precisam ser copiados. Como um aparte, você deve favorecer a imutabilidade por esse motivo.fonte
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:
Restaure sua classe a partir de um fluxo de bytes:
fonte
instance
nesse caso?Você pode criar um clone profundo baseado em serialização usando o
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang, mas tenha cuidado - o desempenho é péssimo.Em geral, é uma prática recomendada escrever seus próprios métodos de clone para cada classe de um objeto no gráfico de objetos que precisam de clonagem.
fonte
org.apache.commons.lang.SerializationUtils
Uma maneira de implementar a 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 argumento único e copia todos os valores dele. Bastante trabalho, mas bem direto e seguro.
EDIT: observe que você não precisa usar métodos de acesso para ler os 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:
Editar: Observe que, ao usar construtores de cópia, você precisa conhecer 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 (poderá fazê-lo com algum código de reflexão).
fonte
Toyota
, seu código colocará umCar
na lista de destinos. A clonagem adequada geralmente exige que a classe forneça um método de fábrica virtual cujo contrato declare que ele retornará um novo objeto de sua própria classe; o próprio contratante de cópia deveprotected
garantir que ele seja usado apenas para construir objetos cujo tipo preciso corresponda ao do objeto que está sendo copiado).Você pode usar uma biblioteca que possui uma API simples e executa uma clonagem relativamente rápida com reflexão (deve ser mais rápida que os métodos de serialização).
fonte
O Apache commons oferece uma maneira rápida de clonar profundamente um objeto.
fonte
O XStream é realmente útil nesses casos. Aqui está um código simples para fazer clonagem
fonte
Uma abordagem muito fácil e simples é usar o Jackson JSON para serializar objetos Java complexos em JSON e lê-los novamente.
http://wiki.fasterxml.com/JacksonInFiveMinutes
fonte
Para usuários do Spring Framework . Usando classe
org.springframework.util.SerializationUtils
:fonte
Para objetos complicados e quando o desempenho não é significativo, eu uso uma biblioteca json, como gson para serializar o objeto em texto json, depois desserializa o texto para obter um novo objeto.
O gson, que com base na reflexão, funciona na maioria dos casos, exceto que os
transient
campos não serão copiados e os objetos com referência circular com causaStackOverflowError
.fonte
Use o XStream ( http://x-stream.github.io/ ). Você pode 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 interface clonável.
fonte
A cópia em profundidade 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 o método Clone. Caso contrário, é impossível fazer uma cópia detalhada com segurança, porque o objeto também pode estar compartilhando recursos que não são de dados (por exemplo, conexões com o banco de dados). Em geral, porém, a cópia profunda é considerada uma prática ruim no ambiente Java e deve ser evitada por meio das práticas de design apropriadas.
fonte
fonte
Eu usei o Dozer para clonar objetos java e é ótimo nisso, a biblioteca Kryo é outra ótima alternativa.
fonte
O BeanUtils faz um ótimo trabalho de clonagem profunda de beans.
fonte
1)
Aqui, sua classe MyPerson e MyAddress deve implementar uma interface serilazable
fonte
Usando Jackson para serializar e desserializar o objeto. Esta implementação não requer que o objeto implemente a classe Serializable.
fonte