ProgrammationDéveloppeur Java

Expliquez les différences entre shallow copy et deep copy en Java. Comment sont-elles implémentées et quand peut-on avoir besoin de les utiliser ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Shallow copy (copie superficielle) crée un nouvel objet, mais copie les références aux objets imbriqués, et non les objets eux-mêmes. Deep copy (copie profonde) crée un nouvel objet et copie de manière récursive tous les objets imbriqués dans l'original, de sorte qu'il n'y a pas de références partagées entre la copie et l'original.

Quand utiliser :

  • La shallow copy est suffisante si la structure de l'objet ne contient pas d'objets imbriqués mutables, ou si les objets imbriqués ne doivent pas être modifiés.
  • La deep copy est nécessaire si la mutabilité des objets imbriqués est critique et que toute modification de la copie ne doit pas se refléter dans l'original.

Exemple d'implémentation :

// Exemple de classe avec un objet imbriqué class Address { String city; Address(String city) { this.city = city; } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } // Shallow copy public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } // Deep copy public Person deepClone() { return new Person(this.name, new Address(this.address.city)); } }

Question piège

Peut-on réaliser un deep copy en utilisant la méthode clone() ?

Réponse : Non, la méthode standard Object.clone() ne réalise qu'une "shallow copy". Pour obtenir une deep copy, il faut soit cloner manuellement tous les champs imbriqués, soit utiliser des bibliothèques tierces (par exemple, Apache Commons Lang).

Exemple :

@Override public Person clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = new Address(this.address.city); // deep copy manuelle return cloned; }

Histoire

Dans un grand projet CRM, après avoir implémenté le clonage d'objets via super.clone(), le développeur s'attendait à l'indépendance complète des copies, mais en modifiant l'adresse de l'utilisateur cloné, l'adresse de l'original changeait également. Le bug a été identifié après la publication et a causé de la confusion dans la base de clients.


Histoire

Lors de la sérialisation/désérialisation pour le clonage profond, le mécanisme standard Serializable a été choisi. Cependant, un des champs d'un objet imbriqué a été oublié d'être déclaré comme Serializable, ce qui a conduit à des erreurs pendant l'exécution (NotSerializableException) et des pertes de données lors de la sauvegarde.


Histoire

Dans l'un des microservices, ils ont utilisé la bibliothèque de clonage BeanUtils.copyProperties pour copier des DTO, sans réaliser que le mécanisme ne réalise qu'une shallow copy. Après le refactoring, il y a eu de nombreux bugs non évidents lorsque les modifications des collections imbriquées affectaient les données originales obtenues de la base.