Avant de suivre officiellement le sujet, comprenons d'abord les concepts de copie profonde et de pré-copie:
Copie légère:
Un nouvel objet sera créé, qui a une copie exacte de la valeur de propriété de l'objet d'origine. Si l'attribut est un type de base, la valeur du type de base est copiée; Si l'attribut est une adresse mémoire, l'adresse mémoire est copiée, donc si un objet modifie cette adresse, elle affectera un autre objet;
Copie profonde:
Non seulement vous souhaitez copier toutes les valeurs de variable de membre non référencées de l'objet, mais vous devez également créer une nouvelle instance pour les variables membre du type de référence et l'initialiser à la valeur d'instance de paramètre formel;
Après avoir compris le concept, testons si les opérations d'attribution d'objets ordinaires sont une copie profonde ou une copie superficielle:
Code de test:
classe publique DepthCopy {public static void main (String [] args) {copy first = new Copy ("hzw", 24); Copier Second = First; second.name = "shanxi"; System.out.println (first.name); // output shanxi}} classe Copie {nom de chaîne publique; L'âge public; Copie publique (nom de chaîne, int age) {this.name = name; this.age = âge; }} On peut constater qu'après la deuxième modification de la valeur d'attribut de nom à Shanxi, la valeur d'attribut de prénom devient également Shanxi. Cela montre que les affectations d'objets ordinaires appartiennent à des copies peu profondes;
Après avoir compris si l'affectation entre les objets est une copie superficielle, voyons si le clonage est une copie profonde ou une copie superficielle. Le code de test consiste à activer l'objet Copie ci-dessus pour implémenter la méthode de clone dans l'interface clonable:
classe publique DepthCopy {public static void main (String [] args) {copy first = new Copy ("hzw", 24); Copier seconde = null; essayez {second = (copy) first.clone (); } catch (clonenotsupportEdException e) {e.printStackTrace (); } second.name = "shanxi"; System.out.println (first.name); // output: hzw System.out.println (premier); // output: com.hzw.day33.copy@7f39ebdb System.out.println (deuxième); // output: com.hzw.day33.copy@33abb81e}} Class Copy Implements Implements cllonable {public chaîne Nom; L'âge public; Copie publique (nom de chaîne, int age) {this.name = name; this.age = âge; } @Override Protected Object Clone () lève CLONENOTSUPPORTEDException {return super.clone (); }}On peut voir que l'objet créé à l'origine en premier et l'objet cloné deuxième sont deux instances, de sorte que la modification de l'attribut de nom en seconde n'affectera pas l'attribut de nom dans First; Cependant, nous ne pouvons pas simplement penser que le clonage est une copie profonde, comme l'exemple suivant:
classe publique DepthCopy {public static void main (String [] args) {étudiant étudiant = nouveau étudiant (95); Copy First = new Copy ("HZW", 24, étudiant); Copier seconde = null; essayez {second = (copy) first.clone (); } catch (clonenotsupportEdException e) {e.printStackTrace (); } second.name = "shanxi"; second.student.score = 60; System.out.println (first == second); // false System.out.println (first.student == second.student); // true system.out.println (first.student.score); // 60}} La copie de classe implémente clonéable {nom de chaîne publique; L'âge public; étudiant public public; Copie publique (nom de la chaîne, Int Age, étudiant étudiant) {this.name = name; this.age = âge; this.Student = étudiant; } @Override Protected Object Clone () lève CLONENOTSUPPORTEDException {return super.clone (); }} classe Student {public int score; Student public (int score) {this.score = score; }}L'avez-vous vu? Nous avons créé la seconde à travers le clonage, et il est évident que les premiers et les secondes sont deux instances, car la sortie de premier == seconde est fausse, mais les objets étudiants en premier et deuxième sont les mêmes. Après avoir modifié la valeur du score de l'élève au deuxième rang, le score de l'élève en premier a également changé. Cela signifie que l'étudiant en premier et deuxième est le même. Cela signifie que le clonage est une copie superficielle. Si nous voulons mettre en œuvre une copie profonde du clonage, nous devons laisser l'objet étudiant dans l'objet Copie implémenter également la méthode de clone dans l'interface clonable, et la méthode de clone en copie renvoie un clone d'étudiant, afin que l'étudiant puisse être unique. Le code modifié est le suivant:
classe publique DepthCopy {public static void main (String [] args) {étudiant étudiant = nouveau étudiant (95); Copy First = new Copy ("HZW", 24, étudiant); Copier seconde = null; essayez {second = (copy) first.clone (); } catch (clonenotsupportEdException e) {e.printStackTrace (); } second.name = "shanxi"; second.student.score = 60; System.out.println (premier == second); // false System.out.println (first.student == second.student); // false system.out.println (first.student.score); // 95 System.out.println (seconde.student.score); // 60}} Class Copy Implementation Cloneable {Public String Name; L'âge public; étudiant public public; Copie publique (nom de la chaîne, Int Age, étudiant étudiant) {this.name = name; this.age = âge; this.Student = étudiant; } @Override Protected Object Clone () lève ClonenotsupporTedException {copy copy = (copy) super.clone (); copy.student = (étudiant) student.clone (); retourner la copie; }} La classe Student implémente Clonable {public int score; Student public (int score) {this.score = score; } @Override Protected Object Clone () lève CLONENOTSUPPORTEDException {return super.clone (); }} Vous pouvez voir que le premier et le deuxième, le premier.Student et le second.Sudent ne sont pas les mêmes pour le moment. Par conséquent, après avoir modifié le score du deuxième élève, cela n'affecte pas la valeur du score de l'élève dans le premier, atteignant l'objectif de la copie en profondeur;
Cependant, si vous y réfléchissez attentivement, le problème se posera. S'il existe également des attributs de types de référence dans la classe étudiante dans notre exemple ci-dessus, comme la classe collégiale, nous devons laisser la classe collégiale mettre en œuvre l'interface clonable, puis appeler la méthode de clone de classe collégiale dans la méthode de clone dans la classe étudiante, et appeler la méthode de clone de classe étudiante dans la méthode clone de la classe de copie. J'ai trouvé que c'était parti. Ce processus est tellement compliqué. Tous les types de référence pertinents dans la classe doivent implémenter l'interface clonable. Je pense que c'est tellement gênant, d'accord, la prochaine chose est d'être génial.
La meilleure façon de résoudre le problème de copie profonde est d'utiliser la sérialisation, afin que toutes les classes n'aient pas besoin d'implémenter l'interface clonable, de sérialiser et de désérialiser directement. Jetons un coup d'œil.
Importer java.io.file; import java.io.fileInputStream; Importer java.io.fileOutputStream; import java.io.objectInputStream; Importer java.io.ObjectOutputStream; import java.io.serializable; classe publique DepthCopy {public static void main (String [] args) {collège scolaire = nouveau collège ("nongda"); Étudiant étudiant = nouvel élève (95, école); Copie copie = new Copy ("HZW", 23, étudiant); Copier un autre = null; // indique l'instance de classe désérialisée // sérialiser l'opération de sérialisation try {fileOutputStream fos = new FileOutputStream (new File ("d: /copy.txt")); ObjectOutputStream oos = new ObjectOutputStream (fos); oos.writeObject (copie); } catch (exception e) {e.printStackTrace (); } // sérialiser l'opération de désérialisation FILEInputStream fis; try {fis = new FileInputStream (nouveau fichier ("d: /copy.txt")); ObjectInputStream oiS = new ObjectInputStream (FIS); un autre = (copie) ois.readObject (); } catch (exception e) {e.printStackTrace (); } System.out.println (copy == un autre); // false System.out.println (copy.student == Another.Student); // false System.out.println (copy.student.school == Another.Student.school); // false un autre.Student.school.schoolname = "wuda"; System.out.println (copy.student.school.schoolname); // nongda}} Class Copy implémente Serializable {public String Name; L'âge public; étudiant public public; Copie publique (nom de la chaîne, Int Age, étudiant étudiant) {this.name = name; this.age = âge; this.Student = étudiant; }} classe étudiant implémente Serializable {public int score; École de collège publique; Étudiant public (score int, école collégiale) {this.score = score; this.school = école; }} Class College implémente Serializable {public String SchoolName; Collège public (String SchoolName) {this.schoolName = scolaire; }} À partir de la sortie, nous pouvons voir que l'objet généré après désérialisation est une copie de l'objet d'origine, et qu'il n'a aucune relation avec l'objet d'origine, sauf pour la même valeur d'attribut. Par conséquent, lorsque nous modifions le nom d'école de l'objet généré de désérialisation à "Wuda", nous n'avons pas modifié le nom d'école de l'instance originale, et nous avons toujours sorti "Nongda", nous avons donc atteint le véritable effet de copie profonde. Cependant, afin d'atteindre la sérialisation, toutes les classes pertinentes doivent implémenter l'interface sérialisable, ce qui est toujours plus pratique que d'implémenter à la fois l'interface clonable et la méthode de clone.
Ce qui précède est une explication détaillée des copies profondes et peu profondes de Java. Si vous en avez besoin, veuillez y consulter.