885 votes

Comment 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 a dumtwo et le changement dum sans affecter le dumtwo . Mais le code ci-dessus ne le fait pas. Lorsque je change quelque chose dans dum le même changement se produit dans dumtwo également.

Je suppose que, quand je dis dumtwo = dum Java copie le référence seulement . Existe-t-il un moyen de créer une nouvelle copie de la base de données de l'entreprise ? dum et l'affecter à dumtwo ?

678voto

egaga Points 5367

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 aussi une méthode clone qui peut être utilisée pour copier l'objet, mais ne l'utilisez pas. Il est bien trop facile de créer une classe et d'utiliser une méthode clone inappropriée. Si vous voulez le faire, lisez au moins ce que Joshua Bloch a à dire à ce sujet dans le document suivant _Java efficace_ .

45 votes

Mais il devrait alors changer son code en DummyBean two = new DummyBean(one) ; n'est-ce pas ?

13 votes

Cette méthode permet-elle d'obtenir le même résultat qu'une copie en profondeur ?

135 votes

@MatthewPiziak, pour moi, il ne s'agirait pas d'un clone profond puisque tous les objets imbriqués feraient toujours référence à l'instance source originale, et non d'un double, à moins que chaque objet de référence (de type non-valeur) fournisse le même modèle de constructeur que ci-dessus.

440voto

Chandra Sekhar Points 5388

De base : Copie d'objets en Java.

Supposons un objet obj1 qui contient deux objets, contenuObj1 y contenuObj2 .
enter image description here

copie superficielle :
instance de la même classe, copie tous les champs dans la nouvelle instance et la renvoie. Classe d'objets fournit un clone et fournit un support pour la copie superficielle.
enter image description here

Copie profonde :
Une copie profonde se produit lorsque un objet est copié avec les objets auxquels il se réfère. . L'image ci-dessous montre obj1 après qu'une copie profonde ait été effectuée sur elle. Non seulement obj1 ont été copiés mais les objets qu'il contient ont également été copiés. Nous pouvons utiliser Java Object Serialization pour faire une copie profonde. Malheureusement, cette approche présente aussi quelques problèmes( exemples détaillés ).
enter image description here

Problèmes possibles :
clone est difficile à mettre en œuvre correctement.
Il est préférable d'utiliser Copie défensive , constructeurs de copie (comme la réponse de @egaga) ou Méthodes d'usine statiques .

  1. Si vous avez un objet, dont vous savez qu'il a un public clone() mais que vous ne connaissez pas le type de l'objet au moment de la compilation, alors vous avez un problème. Java possède une interface appelée Cloneable . En pratique, nous devrions implémenter cette interface si nous voulons qu'un objet Cloneable . Object.clone est protégé donc nous devons contourner avec une méthode publique pour qu'elle soit accessible.

  2. Un autre problème se pose lorsque nous essayons copie profonde d'un objet complexe . Supposons que le clone() de toutes les variables d'objets membres fait également une copie profonde, c'est une hypothèse trop risquée. Vous devez contrôler le code dans toutes les classes.

Par exemple org.apache.commons.lang.SerializationUtils aura une méthode pour le clone profond en utilisant la sérialisation ( Source : ). Si nous avons besoin de cloner un haricot, il existe quelques méthodes utilitaires dans le module org.apache.commons.beanutils ( Source : ).

  • cloneBean Clonera un bean en se basant sur les getters et setters de propriété disponibles, même si la classe du bean elle-même n'implémente pas Cloneable.
  • copyProperties copiera les valeurs des propriétés du bean d'origine vers le bean de destination pour tous les cas où les noms des propriétés sont les mêmes.

1 votes

Pouvez-vous expliquer ce qu'est un objet contenu dans un autre ?

1 votes

@Chandra Sekhar "La copie superficielle crée une nouvelle instance de la même classe et copie tous les champs dans la nouvelle instance et la renvoie" c'est faux de mentionner tous les champs, parce que les objets ne sont pas copiés seulement les références sont copiées qui pointent vers le même objet que l'ancien (original) pointait.

4 votes

@sunny - La description de Chandra est correcte. Et votre description de ce qui se passe l'est aussi ; je dis que vous avez une mauvaise compréhension de la signification de "copie tous les champs". Le champ est la référence, ce n'est pas l'objet auquel on fait référence. "copier tous les champs" signifie "copier toutes ces références". C'est bien que vous ayez indiqué ce que cela signifie exactement, pour toute autre personne qui a la même mauvaise interprétation que vous, de l'énoncé "copier tous les champs" :)

151voto

pacheco Points 161

Dans le paquet import org.apache.commons.lang.SerializationUtils; il y a une méthode :

SerializationUtils.clone(Object);

Ejemplo:

this.myObjectCloned = SerializationUtils.clone(this.object);

72 votes

Tant que l'objet implémente Serializable

2 votes

Dans ce cas, l'objet cloné n'a pas de référence à l'original, si le dernier est statique.

10 votes

Une bibliothèque tierce juste pour cloner l'objet !

109voto

Bhasker Tiwari Points 321

Il suffit de suivre les instructions 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, il suffit d'effectuer 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

1 votes

L'avez-vous testé ? Je pourrais l'utiliser pour mon projet et il est important qu'il soit correct.

2 votes

@misty Je l'ai testé. Il fonctionne parfaitement sur mon application de production.

0 votes

Après le clonage, lorsque vous modifiez l'objet original, le clone est également modifié.

46voto

WillingLearner Points 95

Pourquoi n'y a-t-il pas de réponse pour l'utilisation de l'API 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 très simple.

EDIT : Inclure l'objet enfant par récursion

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

0 votes

Cela semble beaucoup mieux, mais vous ne devez prendre en compte que les champs finaux, car setAccessible(true) peut échouer, et vous devez donc peut-être gérer séparément l'exception IllegalAccessException déclenchée lors de l'appel de field.set(clone, field.get(obj)).

1 votes

Je l'ai beaucoup aimé mais pouvez-vous le remanier pour utiliser des génériques ? private static <T>T cloneObject(T obj) { .... }

2 votes

Je pense que c'est un problème lorsque nous avons des références de propriétés à leurs parents : Class A { B child; } Class B{ A parent; }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X