Como faço para copiar um objeto em Java?
Pergunta
Considere o código abaixo:
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'
Então, eu quero copiar o dum
para dumtwo
e mudança dum
sem afetar o dumtwo
. Mas o código acima não está fazendo isso. Quando eu mudar alguma coisa em dum
, a mesma mudança está acontecendo em dumtwo
também.
Eu acho que, quando eu digo dumtwo = dum
, cópias Java referência somente . Então, há alguma maneira de criar uma nova cópia do dum
e atribuí-la a dumtwo
?
Solução
Criar um construtor de cópia:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
Cada objeto tem também um método clone que pode ser usado para copiar o objeto, mas não usá-lo. É muito fácil criar uma classe e fazer método clone imprópria. Se você estiver indo para fazer isso, leia pelo menos o que Joshua Bloch tem a dizer sobre isso em Effective Java .
Outras dicas
básica:. Objeto Copiar em Java
Vamos assumir uma obj1
a objetos, que contém dois objetos, containedObj1 e containedObj2 .
rasa copiar:
cópia rasa cria um novo instance
da mesma classe e copia todos os campos para a nova instância e devolve-lo. classe Object fornece um método clone
e fornece suporte para a cópia superficial.
Deep copiar:
Uma cópia profunda ocorre quando um objeto é copiado juntamente com os objetos a que se refere . Abaixo mostra a imagem obj1
depois de uma cópia profunda foi realizada nele. Não só tem sido obj1
Copiado , mas os objetos contidos dentro dele foram copiados também. Podemos usar Java Object Serialization
para fazer uma cópia profunda. Infelizmente, esta abordagem tem alguns problemas também ( exemplos detalhados ) .
Problemas possíveis:
clone
é complicado para implementar corretamente.
É melhor usar a cópia defensiva , copiar construtores (como resposta @egaga) ou métodos de fábrica estáticos .
- Se você tem um objeto, que você conhece tem um método
clone()
público, mas você não sabe o tipo de objeto em tempo de compilação, então você tem problema. Java tem uma interface chamadaCloneable
. Na prática, devemos implementar essa interface, se quisermos fazer umaCloneable
objeto.Object.clone
é protegido , por isso temos de substituir -lo com um método público para que ele seja acessível.
- Outro problema surge quando tentamos cópia profunda de um objeto complexo . Suponha que o método
clone()
de todas as variáveis ??membro do objeto também faz cópia profunda, isso é muito arriscado de uma suposição. Você deve controlar o código em todas as classes.
Por exemplo org.apache.commons.lang.SerializationUtils terá método para Deep clone usando serialização ( Fonte ). Se precisamos clone feijão, em seguida, existem alguns métodos utilitários em org.apache.commons.beanutils ( Fonte ).
-
cloneBean
irá clonar um feijão com base nos getters de propriedade disponíveis e setters, mesmo se a classe de feijão em si não implementa Cloneable. -
copyProperties
irá copiar os valores de propriedade do feijão origem ao feijão destino para todos os casos em que os nomes das propriedades são os mesmos.
No import org.apache.commons.lang.SerializationUtils;
pacote há um método:
SerializationUtils.clone(Object);
Exemplo:
this.myObjectCloned = SerializationUtils.clone(this.object);
Basta seguir como abaixo:
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();
}
}
e onde quer que você deseja obter um outro objeto, simples executar a clonagem. por exemplo:
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
Por que não há resposta 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;
}
}
É realmente simples.
EDIT: Incluir objeto filho via recursão
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;
}
}
Eu uso biblioteca JSON do Google para serializar-lo em seguida, criar uma nova instância do objeto serializado. Ele faz cópia profunda com algumas restrições:
-
não pode haver qualquer referência recursiva
-
não vai copiar matrizes de tipos diferentes
-
matrizes e listas devem ser digitados ou não vai encontrar a classe para instanciar
-
Você pode precisar de cordas Encapsular em uma classe que você declarar-se
Eu também usar essa classe para salvar as preferências do usuário, janelas e outros enfeites para ser recarregado em tempo de execução. É muito fácil de usar e eficaz.
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;
}
}
Sim, você está apenas fazendo uma referência para o objeto. Você pode clonar o objeto se ele implementa Cloneable
.
Confira este artigo wiki sobre como copiar objetos.
Sim. Você precisa profunda Copiar seu objeto.
Adicionar Cloneable
e código abaixo para sua classe
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Use este clonedObject = (YourClass) yourClassObject.clone();
Isso funciona também. Assumindo modelo
class UserAccount{
public int id;
public String name;
}
Primeiro add
compile 'com.google.code.gson:gson:2.8.1'
ao seu aplicativo> Gradle e sincronização. Então
Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);
Você pode excluir usando um campo usando palavra-chave transient
após modificador de acesso.
Nota: Esta é uma prática ruim. Também não recomendamos a utilização Cloneable
ou JavaSerialization
É lento e quebrado. Escrever construtor de cópia para o melhor desempenho ref .
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);
}
}
estatísticas de teste de 90000 iteração:
UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
linha leva 808ms
Linha UserAccount clone = new UserAccount(aO);
leva menos de 1 ms
Conclusão: Use Gson se seu chefe é louco e você prefere velocidade. Use segundo construtor de cópia se você preferir qualidade.
Você também pode usar o código de construtor de cópia gerador de plugin no Android Studio.
Aqui está uma explicação decente de clone()
se você acabar precisando dele ...
Deep clonagem é a sua resposta, o que requer a implementação da interface Cloneable
e substituindo o método 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;
}
}
Você vai chamá-lo assim
DummyBean dumtwo = dum.clone();
Use um utilitário de clonagem profunda:
SomeObjectType copy = new Cloner().deepClone(someObject);
Isso irá copiar profunda qualquer objeto java, verificá-la em https://github.com/kostaskougios/cloning
Para fazer isso você tem que clonar o objeto de alguma forma. Embora Java tem um mecanismo de clonagem, não usá-lo se você não tem que. Criar um método de cópia que faz o trabalho de cópia para você, e, em seguida, fazer:
dumtwo = dum.copy();
Aqui é mais alguns conselhos sobre técnicas diferentes para a realização de uma cópia.
Além de copiar explicitamente, uma outra abordagem é fazer o imutável objeto (sem set
ou outros métodos modificadores). Desta forma, a questão nunca surge. Imutabilidade torna-se mais difícil com objetos maiores, mas que o outro lado disso é que ele empurra-lo na direção de dividir em pequenos objetos e compósitos coerentes.
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
Você pode profunda copiar automaticamente com XStream, de http://x-stream.github.io/:
XStream é uma biblioteca simples de objetos serialize para XML e volta novamente.
Adicione ao seu projeto (se estiver usando Maven)
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
Em seguida
DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));
Com isso, você tem uma cópia, sem a necessidade de implementar qualquer interface de clonagem.
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();
}
}
}
e em seu código:
MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();
Passe o objeto que você deseja copiar e obter o objeto que você deseja:
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;
}
Agora analisar o objDest
ao objeto desejado.
Happy Coding!
Você pode tentar implementar Cloneable
e usar o método clone()
; No entanto, se você usar o método clone você deve - por padrão -. método Object
sempre substituem do public Object clone()
Se você pode adicionar uma anotação ao arquivo de origem, um gerador de processador de anotação ou código como esta pode ser usado.
import net.zerobuilder.BeanBuilder
@BeanBuilder
public class DummyBean {
// bean stuff
}
A DummyBeanBuilders
classe será gera, que tem um dummyBeanUpdater
método estático para criar cópias rasas, da mesma forma que você iria fazê-lo manualmente.
DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();