Comment faites-vous une copie profonde d’un object en Java?

En Java, il est un peu difficile d’implémenter une fonction de copie d’object approfondie. Quelles mesures prenez-vous pour vous assurer que l’object d’origine et le cloné ne partagent aucune référence?

Un moyen sûr consiste à sérialiser l’object, puis à le désérialiser. Cela garantit que tout est une nouvelle référence.

Voici un article sur la façon de le faire efficacement.

Mises en garde: Il est possible que les classes remplacent la sérialisation de sorte que de nouvelles instances ne soient pas créées, par exemple pour les singletons. Bien sûr, cela ne fonctionne pas si vos classes ne sont pas sérialisables.

Quelques personnes ont mentionné l’utilisation ou la Object.clone() . Ne le fais pas Object.clone() a quelques problèmes majeurs et son utilisation est déconseillée dans la plupart des cas. S’il vous plaît voir l’article 11, de ” Java efficace ” par Joshua Bloch pour une réponse complète. Je pense que vous pouvez utiliser Object.clone() en toute sécurité sur les tableaux de type primitif, mais à part cela, vous devez être Object.clone() utilisation et le Object.clone() correct du clone.

Les schémas qui reposent sur la sérialisation (XML ou autre) sont compliqués.

Il n’y a pas de réponse facile ici. Si vous souhaitez copier en profondeur un object, vous devrez parcourir le graphe d’object et copier chaque object enfant explicitement via le constructeur de copie de l’object ou une méthode de fabrique statique qui à son tour copie l’object enfant. Les immutables (par exemple, Ssortingng s) n’ont pas besoin d’être copiés. En passant, vous devriez privilégier l’immuabilité pour cette raison.

Vous pouvez faire une copie profonde avec la sérialisation sans créer de fichiers.

Votre object que vous souhaitez copier en profondeur devra implement serializable . Si la classe n’est pas définitive ou ne peut pas être modifiée, étendez la classe et implémentez serializable.

Convertissez votre classe en un stream d’octets:

 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.flush(); oos.close(); bos.close(); byte[] byteData = bos.toByteArray(); 

Restaurez votre classe à partir d’un stream d’octets:

 ByteArrayInputStream bais = new ByteArrayInputStream(byteData); (Object) object = (Object) new ObjectInputStream(bais).readObject(); 

Vous pouvez faire un clone profond basé sur la sérialisation en utilisant org.apache.commons.lang3.SerializationUtils.clone(T) dans Apache Commons Lang, mais faites attention, les performances sont désastreuses.

En règle générale, il est recommandé d’écrire vos propres méthodes de clonage pour chaque classe d’un object du graphe d’object nécessitant un clonage.

Une manière d’implémenter la copie profonde consiste à append des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de ‘this’ comme argument unique et en copie toutes les valeurs. Un peu de travail, mais assez simple et sûr.

EDIT: notez que vous n’avez pas besoin d’utiliser des méthodes d’access pour lire les champs. Vous pouvez accéder directement à tous les champs car l’instance source est toujours du même type que l’instance avec le constructeur de copie. Évident mais pourrait être négligé.

Exemple:

 public class Order { private long number; public Order() { } /** * Copy constructor */ public Order(Order source) { number = source.number; } } public class Customer { private Ssortingng name; private List orders = new ArrayList(); public Customer() { } /** * Copy constructor */ public Customer(Customer source) { name = source.name; for (Order sourceOrder : source.orders) { orders.add(new Order(sourceOrder)); } } public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } } 

Edit: Notez que lorsque vous utilisez des constructeurs de copie, vous devez connaître le type d’exécution de l’object que vous copiez. Avec l’approche ci-dessus, vous ne pouvez pas facilement copier une liste mixte (vous pourrez peut-être le faire avec un code de reflection).

Apache commons offre un moyen rapide de cloner en profondeur un object.

 My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1); 

Vous pouvez utiliser une bibliothèque dotée d’une API simple et effectuer un clonage relativement rapide avec reflection (plus rapide que les méthodes de sérialisation).

 Cloner cloner = new Cloner(); MyClass clone = cloner.deepClone(o); // clone is a deep-clone of o 

XStream est vraiment utile dans de tels cas. Voici un code simple pour faire du clonage

 private static final XStream XSTREAM = new XStream(); ... Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj)); 

Une approche simple et très simple consiste à utiliser Jackson JSON pour sérialiser un object Java complexe en JSON et le lire.

http://wiki.fasterxml.com/JacksonInFiveMinutes

Utilisez XStream ( http://x-stream.github.io/ ). Vous pouvez même contrôler les propriétés que vous pouvez ignorer via des annotations ou spécifier explicitement le nom de la propriété à la classe XStream. De plus, vous n’avez pas besoin d’implémenter une interface clonable.

La copie en profondeur ne peut être effectuée qu’avec le consentement de chaque classe. Si vous contrôlez la hiérarchie des classes, vous pouvez implémenter l’interface clonable et implémenter la méthode Clone. Sinon, il est impossible de faire une copie en toute sécurité car l’object peut également partager des ressources autres que des données (par exemple, des connexions à une firebase database). En général, la copie en profondeur est considérée comme une mauvaise pratique dans l’environnement Java et doit être évitée via les pratiques de conception appropriées.

 import com.thoughtworks.xstream.XStream; public class deepCopy { private static XStream xstream = new XStream(); //serialize with Xstream them deserialize ... public static Object deepCopy(Object obj){ return xstream.fromXML(xstream.toXML(obj)); } } 

J’ai utilisé Dozer pour cloner des objects Java et c’est génial, la bibliothèque Kryo est une autre excellente alternative.

Pour les utilisateurs de Spring Framework . En utilisant la classe org.springframework.util.SerializationUtils :

 @SuppressWarnings("unchecked") public static  T clone(T object) { return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object)); } 

Pour les objects compliqués et lorsque les performances ne sont pas significatives, j’utilise une bibliothèque json, comme gson pour sérialiser l’object en texte json, puis désérialiser le texte pour obtenir un nouvel object.

gson qui, sur la base de la reflection, fonctionnera dans la plupart des cas, sauf que transient champs transient ne seront pas copiés et les objects avec référence circulaire avec la cause StackOverflowError .

 public static  T Copy(T AnObject, Class ClassInfo) { Gson gson = new GsonBuilder().create(); Ssortingng text = gson.toJson(AnObject); T newObject = gson.fromJson(text, ClassInfo); return newObject; } public static void main(Ssortingng[] args) { Ssortingng originalObject = "hello"; Ssortingng copiedObject = Copy(originalObject, Ssortingng.class); } 

1)

 public static Object deepClone(Object object) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(object); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } 2) // (1) create a MyPerson object named Al MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India"); MyPerson al = new MyPerson("Al", "Arun", address); // (2) make a deep clone of Al MyPerson neighbor = (MyPerson)deepClone(al); 

Ici, votre classe MyPerson et MyAddress doit implémenter une interface sérieuse

BeanUtils fait un très bon travail de clonage en profondeur des haricots.

 BeanUtils.cloneBean(obj);