Comment puis-je copier un objet en Java?
Considérez 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 le dum
à dumtwo
et dum
sans affecter le dumtwo
. Mais le code ci-dessus ne le fait pas. Quand je change quelque chose dans dum
, le même changement se produit dans dumtwo
aussi.
je suppose que, quand je dis dumtwo = dum
, Java copie le référence seulement . Donc, est-il possible d'en créer une nouvelle une copie de dum
et assignez-la à dumtwo
?
21 réponses
créer un constructeur de copie:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
Chaque objet a également une méthode clone qui peut être utilisé pour copier l'objet, mais ne l'utilisez pas. C'est trop facile de créer une classe et faire une mauvaise méthode clone. Si vous allez faire cela, lisez au moins ce que Joshua Bloch a à dire à ce sujet dans Java efficace .
de Base": Objet de la Copie en Java.
supposons un objet - obj1
, qui contient deux objets, containedObj1 et containedObj2 .
copie peu profonde:
la copie superficielle crée un nouveau instance
de la même classe et copie tous les champs de la nouvelle instance et le renvoie. classe d'Objets fournit un clone
méthode et fournit un soutien pour les eaux peu profondes de la copie.
au plus Profond de la copie:
Une copie profonde se produit quand un objet est copié avec les objets auxquels il se réfère . Ci-dessous l'image montre obj1
après une copie profonde a été effectuée sur elle. n'a Pas seulement obj1
copié , mais les objets qu'il contient ont été copiés. Nous pouvons utiliser Java Object Serialization
pour faire une copie profonde. Malheureusement, cette approche pose également quelques problèmes( exemples détaillés ).
Problèmes Possibles:
clone
est difficile à mettre en œuvre correctement.
Il est préférable d'utiliser copie défensive , copy constructors (comme @egaga répondre) ou static factory methods .
- si vous avez un objet, que vous connaissez a une méthode publique
clone()
, mais vous ne savez pas le type de l'objet à compiler temps, alors vous avez un problème. Java a une interface appeléeCloneable
. En pratique, nous devrions implémenter cette interface si nous voulons faire un objetCloneable
.Object.clone
est protégé , nous devons donc outrepasser il avec une méthode publique afin qu'il soit accessible.
- un autre problème se pose lorsque nous essayons copie profonde d'un objet complexe . Supposons que la méthode
clone()
de toutes les variables d'objet membre copie aussi en profondeur, c'est trop risqué d'une hypothèse. Vous devez contrôler le code dans toutes les classes.
Pour l'exemple org.Apache.commun.lang.SerializationUtils aura méthode pour Deep clone en utilisant serialization ( Source ). Si nous avons besoin de cloner alors Il ya quelques méthodes d'utilité dans org.Apache.commun.beanutils ( Source ).
-
cloneBean
Clonera un haricot en fonction de la propriété disponible getters et setters, même si la classe de haricot elle-même ne met pas en œuvre clonable. -
copyProperties
copie les valeurs de propriété de la fève d'origine à la fève de destination pour tous les cas où les noms de propriété sont les mêmes.
dans le paquet import org.apache.commons.lang.SerializationUtils;
il y a une méthode:
SerializationUtils.clone(Object);
exemple:
this.myObjectCloned = SerializationUtils.clone(this.object);
comme suit:
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, simple effectuer le clonage. e.g:
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 à l'utilisation de L'API réflexion?
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 très simple.
EDIT: l'enfance de l'objet 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 la sérialiser puis créer une nouvelle instance de l'objet sérialisé. Il fait une copie profonde avec quelques restrictions:
-
il ne peut pas y avoir de références récursives
-
cela ne peut pas copier des tableaux de types disparates
-
les tableaux et les listes doivent être dactylographiés ou il ne trouvera pas la classe pour instancier
-
vous pourriez avoir besoin d'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, windows et 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 juste référence à l'objet. Vous pouvez cloner l'objet s'il implémente Cloneable
.
consultez cet article wiki sur la copie d'objets.
ajouter Cloneable
et au-dessous du code de votre classe
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
utilisez cette clonedObject = (YourClass) yourClassObject.clone();
voici une bonne explication de clone()
si vous en avez besoin...
ça marche aussi. Modèle hypothétique
class UserAccount{
public int id;
public String name;
}
premier ajouter
compile 'com.google.code.gson:gson:2.8.1'
de votre application>gradle et synchronisation. Puis
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 recommande pas d'utiliser Cloneable
ou JavaSerialization
il est lent et cassé. Écrire constructeur de copie pour obtenir les 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 D'essai de 90000 itération:
Ligne UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
takes 808ms "1519170920
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 deuxième constructeur de copie si vous préférez la qualité.
vous pouvez également utiliser le code du constructeur de copie plugin générateur dans Android Studio.
Pour ce faire vous devez cloner l'objet d'une certaine façon. Bien que Java ait un mécanisme de Clonage, ne l'utilisez pas si vous n'en avez pas besoin. Créez une méthode de copie qui fait le travail de copie pour vous, puis faites:
dumtwo = dum.copy();
ici est un peu plus de conseils sur les différentes techniques pour accomplir une copie.
le clonage profond est votre réponse, qui nécessite la mise en œuvre de l'interface Cloneable
et la suppression 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();
Utiliser un profond utilitaire de clonage:
SomeObjectType copy = new Cloner().deepClone(someObject);
cela va copier profondément n'importe quel objet java, vérifiez à https://github.com/kostaskougios/cloning
outre la copie explicite, une autre approche consiste à rendre l'objet immuable (no set
ou autres méthodes mutatrices). Dans cette optique, la question se pose jamais. L'immuabilité devient plus difficile avec les objets plus grands, mais l'autre côté de cela est qu'il vous pousse dans la direction de la division en petits objets cohérents et des composites.
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
Passer de l'objet qui vous veut copier et obtenir l'objet qui vous veut ,
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;
}
analysez maintenant l'objet le plus obscur à décrire.
Codage Heureux
vous pouvez copier en profondeur automatiquement avec XStream, à partir de http://x-stream.github.io / :
XStream est une bibliothèque simple pour sérialiser des objets vers XML et retour encore.
ajoutez - le à votre projet (si vous utilisez maven)
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
puis
DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));
avec ceci vous avez une copie sans avoir besoin d'implémenter une interface de Clonage.
si vous pouvez ajouter une annotation au fichier source, un processeur d'annotation ou un générateur de code comme 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();