Considérons le code ci-dessous:
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'
Donc, je veux copier la dum
dans dumtwo
et changer dum
sans affecter la dumtwo
. Mais le code ci-dessus ne fait pas cela. Lorsque je change quelque chose dans dum
, le même changement se produit également dans dumtwo
.
Je suppose que, lorsque je dis dumtwo = dum
, Java copie le référence uniquement. Alors, est-il possible de créer une nouvelle copie de dum
et de l’affecter à dumtwo
?
Créez un constructeur de copie:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
Chaque objet possède également une méthode de clonage qui peut être utilisée pour copier l'objet, mais ne l'utilisez pas. C'est trop facile de créer une classe et d'utiliser une méthode de clonage inappropriée. Si vous voulez faire cela, lisez au moins ce que Joshua Bloch en dit dans Effective Java.
Basic: Copie d'objet en Java.
Supposons un objet - obj1
, qui contient deux objets, contientObj1 et contientObj2.
copie superficielle: _
une copie superficielle crée une nouvelle variable instance
de la même classe, copie tous les champs dans la nouvelle instance et la renvoie. Object class fournit une méthode clone
et prend en charge la copie superficielle.
Copie en profondeur:
Une copie profonde se produit lorsque un objet est copié avec les objets auxquels il fait référence. L'image ci-dessous montre obj1
après qu'une copie en profondeur a été effectuée. Non seulement obj1
a été copié, mais les objets qu'il contient ont également été copiés. Nous pouvons utiliser Java Object Serialization
pour faire une copie en profondeur. Malheureusement, cette approche pose également certains problèmes ( exemples détaillés ).
Problèmes possibles:clone
est délicat à mettre en œuvre correctement.
Il vaut mieux utiliser Copie défensive , constructeurs de copie (comme @egaga reply) ou méthodes d'usine statiques .
clone()
, mais que vous ne connaissez pas le type de l’objet au moment de la compilation, vous avez un problème. Java a une interface appelée Cloneable
. En pratique, nous devrions implémenter cette interface si nous voulons créer un objet Cloneable
. Object.clone
est protégé, nous devons donc remplacer par une méthode publique pour qu'elle soit accessible.clone()
de toutes les variables d'objet membre effectue également une copie en profondeur, ce qui est trop risqué pour une hypothèse. Vous devez contrôler le code dans toutes les classes.Par exemple, org.Apache.commons.lang.SerializationUtils aura une méthode pour le clone Deep utilisant la sérialisation ( Source ). Si nous avons besoin de cloner Bean, il existe plusieurs méthodes d’utilité dans org.Apache.commons.beanutils ( Source ).
cloneBean
clonera un bean en fonction des getters et setters de propriétés disponibles, même si la classe de bean elle-même n'implémente pas Cloneable.copyProperties
Copiera les valeurs de propriété du bean Origin vers le bean de destination pour tous les cas où les noms de propriété sont identiques.Le paquetage import org.Apache.commons.lang.SerializationUtils;
contient une méthode:
SerializationUtils.clone(Object);
Exemple:
this.myObjectCloned = SerializationUtils.clone(this.object);
Suivez juste comme ci-dessous:
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();
}
}
et partout où vous voulez obtenir un autre objet, effectuez simplement un clonage . Par exemple:
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
Pourquoi n'y a-t-il pas de réponse à utiliser l'API de Reflection?
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;
}
}
C'est vraiment simple.
EDIT: inclure un objet enfant via la récursivité
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;
}
}
J'utilise la bibliothèque JSON de Google pour le sérialiser, puis créer une nouvelle instance de l'objet sérialisé. Il copie en profondeur avec quelques restrictions:
il ne peut y avoir de références récursives
il ne copie pas les tableaux de types disparates
les tableaux et les listes doivent être tapés ou il ne trouvera pas la classe pour instancier
vous devrez peut-être encapsuler des chaînes dans une classe que vous déclarez vous-même
J'utilise également cette classe pour enregistrer les préférences de l'utilisateur, les fenêtres et tout ce qui ne doit pas être rechargé à l'exécution. Il est très facile à utiliser et efficace.
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;
}
}
Oui, vous faites simplement référence à l'objet. Vous pouvez cloner l'objet s'il implémente Cloneable
.
Consultez cet article du wiki sur la copie d'objets.
Oui. Vous devez Deep Copy votre objet.
Ajoutez le code Cloneable
et ci-dessous à votre classe
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Utilisez ce clonedObject = (YourClass) yourClassObject.clone();
Voici une explication décente de clone()
si vous en avez besoin ...
Cela fonctionne aussi. En supposant modèle
class UserAccount{
public int id;
public String name;
}
Commencez par ajouter compile 'com.google.code.gson:gson:2.8.1'
à votre application> gradez et synchronisez. ensuite
Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);
Vous pouvez exclure l'utilisation d'un champ en utilisant le mot clé transient
après le modificateur d'accès.
Note: C'est une mauvaise pratique. Aussi, ne recommandez pas d'utiliser Cloneable
ou JavaSerialization
C'est lent et cassé. Ecrire un constructeur de copie pour de meilleures performances ref .
Quelque chose comme
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);
}
}
Statistiques de test de l'itération 90000:
Line UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
prend 808ms
La ligne UserAccount clone = new UserAccount(aO);
prend moins de 1ms
Conclusion: Utilisez gson si votre patron est fou et que vous préférez la vitesse. Utilisez le constructeur de deuxième copie si vous préférez la qualité.
Vous pouvez également utiliser le code constructeur de copie générateur plugin dans Android Studio.
Deep Cloning est votre réponse, qui nécessite l'implémentation de l'interface Cloneable
et le remplacement de la méthode 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;
}
}
Vous l'appellerez comme ceci DummyBean dumtwo = dum.clone();
Pour ce faire, vous devez cloner l'objet d'une certaine manière. Bien que Java dispose d'un mécanisme de clonage, ne l'utilisez pas si vous n'y êtes pas obligé. Créez une méthode de copie qui effectue la copie pour vous, puis procédez comme suit:
dumtwo = dum.copy();
Ici est un peu plus de conseils sur différentes techniques pour réaliser une copie.
Utilisez un utilitaire de clonage profond:
SomeObjectType copy = new Cloner().deepClone(someObject);
Ceci permet de copier en profondeur tout objet Java. Consultez-le à l’adresse https://github.com/kostaskougios/cloning
Autre que la copie explicite, une autre approche consiste à rendre l'objet immuable (pas de méthode set
ni d'autres méthodes de mutation). De cette façon, la question ne se pose jamais. L’immuabilité devient plus difficile avec des objets plus grands, mais l’autre aspect est qu’elle vous pousse dans la direction de la scission en petits objets et composites cohérents.
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
Passez l'objet que vous souhaitez copier et récupérez-le:
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;
}
Maintenant, analysez la objDest
à l'objet désiré.
Bon codage!
Vous pouvez copier en profondeur automatiquement avec XStream, depuis http://x-stream.github.io/ :
XStream est une simple bibliothèque permettant de sérialiser des objets en XML et inversement encore.
Ajoutez-le à votre projet (si vous utilisez maven)
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
Ensuite
DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));
Avec cela, vous avez une copie sans avoir besoin d'implémenter une interface de clonage.
Vous pouvez essayer d'implémenter Cloneable
et utiliser la méthode clone()
; Cependant, si vous utilisez la méthode clone, vous devriez, selon la norme, toujours remplacer la méthode Object
's public Object clone()
.
Si vous pouvez ajouter une annotation au fichier source, un processeur d’annotation ou un générateur de code tel que celui-ci peut être utilisé.
import net.zerobuilder.BeanBuilder
@BeanBuilder
public class DummyBean {
// bean stuff
}
Une classe DummyBeanBuilders
sera générée, qui a une méthode statique dummyBeanUpdater
pour créer des copies superficielles, de la même manière que vous le feriez manuellement.
DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();
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();
}
}
}
et dans votre code:
MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();