Concepts de copie profonde et superficielle en Java
La copie superficielle (Shallow Copy) ne copie que les références d'un objet, sans copier l'objet lui-même. L'objet copié et l'original partagent les mêmes sous-objets en mémoire. Lorsqu'on modifie un attribut non primitif d'un des objets, l'attribut correspondant de l'autre objet est également modifié.
La copie profonde (Deep Copy) copie l'objet ainsi que tous ses sous-objets, créant un complètement nouvel objet indépendant. L'objet copié et l'original sont totalement séparés, et la modification d'un des objets n'affecte pas l'autre.
Exemple d'analyse mémoire
Imaginons une classe Employé contenant un nom (String) et une adresse (Adresse) :
class Adresse {
String ville;
// Constructeurs, getters/setters omis pour brièveté
}
class Employe implements Cloneable {
String nom;
Adresse adresse;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Copie superficielle par défaut
}
}
Implémentation de la copie superficielle et son comportement mémoire
Employe e1 = new Employe();
e1.nom = "Charles";
e1.adresse = new Adresse("Paris");
Employe e2 = (Employe)e1.clone();
Comportement mémoire :
e1 et e2 sont deux objets distincts, mais leurs attributs adresse pointent vers le même objet Adresse. La modification de e2.adresse.ville affectera également e1.adresse.ville.
Méthodes d'implémentation de la copie profonde
Méthode 1 : Surcharge de clone()
@Override
protected Object clone() throws CloneNotSupportedException {
Employe clone = (Employe)super.clone();
clone.adresse = (Adresse)this.adresse.clone(); // Clonage récursif des objets référencés
return clone;
}
Méthode 2 : Implémentation par sérialisation
public Employe copieProfonde() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Employe)ois.readObject();
}
Comportemetn mémoire :
e1 et e2 sont complètement indépendants, y compris pour tous les attributs de type référence. La modification de e2.adresse.ville n'affectera pas e1.adresse.ville.
Performance et cas d'utilisation
Copie superficielle :
Meilleure performance, ne copiee que les références. Idéale lorsque les objets référencés sont immuables ou lorsque leur modification indépendante n'est pas nécessaire.
Copie profonde :
Coût en performance plus élevé, nécessite une copie récursive de tous les objets. Idéale lorsque des objets complètement indépendants sont nécessaires, comme dans les environnements multithreads.
Points importants à considérer
Les objets immuables comme String sont sûrs lors d'une copie superficielle. La méthode clone() pour les tableaux effectue une copie superficielle. La copie profonde peut causer des problèmes de références circulaires. L'utilisation de bibliothèques tierces (comme SerializationUtils d'Apache Commons Lang) peut simplifier l'implémentation de la copie profonde.