Préface
Le clonage Java (clone) est l'une des caractéristiques de la langue java, mais il est rare de l'utiliser dans la pratique. Mais parfois, le clonage est plus pratique et efficace.
Pour les clones, Java a quelques limites:
1. La classe clonée doit implémenter l'interface clonable elle-même pour indiquer que Object.clone() peut légalement copier l'instance de cette classe par champ. L'interface clonable est en fait une interface d'identification et n'a pas de méthode d'interface.
2. Les classes qui implémentent l'interface clonable doivent remplacer Object.clone (il est protégé) en utilisant des méthodes publiques. Il est impossible pour un objet de le cloner après implémentation de cette interface. Même si la méthode de clone est appelée réflexion, rien ne garantit qu'elle réussira.
3. La méthode de clonage de Java.lang.Object est définie comme suit:
Clone d'objet protégé () lève ClonenotsupportEdException
Crée et renvoie une copie de cet objet. Il indique qu'il s'agit d'une méthode protégée, visible dans le même package.
Par convention, l'objet retourné doit être obtenu en appelant super.clone .
Affectation en java
En Java, l'affectation est très couramment utilisée, une simple affectation est la suivante
// Le type d'origine int a = 1; int b = a; // type de référence String [] Weekdays = new String [5]; String [] gongzuori = Weekdays; // Copier uniquement les références
Dans le code ci-dessus.
1. S'il s'agit du type de données d'origine, la valeur réalisée par l'affectation est la valeur réelle.
2. S'il s'agit d'un type de données de référence, l'affectation transmet la référence à l'objet, pas l'objet.
Comprendre la différence entre les types de données et les types de référence nous permet de comprendre plus facilement le clone.
Cloner
Dans Java, Clone est le processus de copie d'un objet existant en mémoire à un autre objet qui est le même que celui-ci. Le clonage dans Java est une copie de domaine par domaine.
Si vous souhaitez prendre en charge les méthodes de clone en Java, vous devez d'abord implémenter l'interface clonable
Cloneable est en fait un peu étrange. C'est différent de l'interface que nous utilisons souvent. Il ne contient aucune méthode à l'intérieur, ce n'est qu'une interface de marquage.
Le code source est le suivant
interface publique clonable {}Sur clonable, à quoi il faut faire attention
1. Si vous souhaitez prendre en charge le clone, vous devez implémenter l'interface clonable
2. Si la méthode de clone n'est pas appelée à l'interface clonable, une clonenotsupportedException sera lancée.
Puis réécrivez la méthode du clone et modifiez-la au niveau d'accès public
classe statique clonable implémente clonable {public int count; enfant public enfant; @Override public Object Clone () lève ClonenotsupporTedException {return super.clone (); }}Méthode du clone d'appel pour copier l'objet
CloneableImp Imp1 = new ClonableImp (); Imp1.Child = new Child ("Andy"); try {objet obj = imp1.clone (); CloneableImp Imp2 = (clonablemp) obj; System.out.println ("Main Imp2.Child.Name =" + Imp2.Child.Name);} Catch (ClonenotsUpporTedException e) {e.printStackTrace ();}Copie légère
Le clone implémenté dans le code ci-dessus est en fait une copie peu profonde.
Ce que vous devez savoir sur une copie superficielle
1. Utilisez la méthode du clone par défaut
2. Faire une copie de valeur du champ de données d'origine
3. Copier uniquement les références pour les types de référence
4. Exécution rapide et grande efficacité
5. Les données ne peuvent pas être séparées de 100%.
6. Si un objet ne contient que le domaine de données d'origine ou le domaine d'objet immuable, il est recommandé d'utiliser une copie superficielle.
En ce qui concerne l'incapacité de séparer les données, nous pouvons utiliser ce code pour les vérifier.
CloneableImp Imp1 = new ClonableImp (); Imp1.Child = new Child ("Andy"); try {objet obj = imp1.clone (); CloneableImp Imp2 = (clonablemp) obj; Imp2.child.name = "bob"; System.out.println ("Main Imp1.Child.Name =" + Imp1.Child.Name);} Catch (ClonenotsUpporTedException e) {e.printStackTrace ();} Dans le code ci-dessus, nous avons utilisé la méthode du clone d'Imp1 pour clone Imp2, puis modifié imp2.child.name à Bob, puis imprimé imp1.child.name pour obtenir le résultat.
Main Imp1.child.name = Bob
La raison en est que la copie peu profonde n'atteint pas une séparation à 100% des données. Imp1 et Imp2 partagent le même objet enfant, donc une modification affectera l'autre.
Copie profonde
Une copie profonde peut résoudre le problème de la séparation à 100% des données. Faites simplement quelques modifications du code ci-dessus.
1. L'enfant implémente l'interface clonable.
Classe publique Child implémente clonable {nom de chaîne publique; Public Child (String Name) {this.name = name; } @Override public String toString () {return "child [name =" + name + "]"; } @Override Protected Object Clone () lève CLONENOTSUPPORTEDException {return super.clone (); }}2. Réécrivez la méthode du clone et appelez la méthode du clone du domaine de données.
classe statique clonable implémente clonable {public int count; enfant public enfant; @Override public Object Clone () lève ClonenotsupporTedException {ClonableImp obj = (ClonableImp) super.clone (); obj.child = (enfant) child.clone (); retour obj; }} Lorsque nous modifions imp2.child.name à nouveau, cela n'affectera pas la valeur d' imp1.child.name , car Imp1 et Imp2 ont chacun leurs propres objets enfants, car les données sont isolées à 100%.
Quelques fonctionnalités de copie profonde
1. Vous devez remplacer la méthode du clone, non seulement appeler la méthode de la classe parent, mais également appeler la méthode du clone de l'attribut.
2. La séparation de données à 100% entre l'objet d'origine et l'objet cloné est atteint
3. Si l'objet a un attribut de type de référence, il est recommandé d'utiliser une copie profonde.
4. La copie profonde est plus longue et moins efficace que la copie superficielle
Pourquoi utiliser le clonage
Il est très important et commun: une API doit fournir une collection de liste, mais elle ne veut pas que la modification de l'appelant affecte ses propres modifications, il doit donc cloner un objet pour atteindre l'objectif de l'isolement des données.
Cloone doit être évité autant que possible
1. Habituellement, l'interface est implémentée pour montrer ce que la classe peut faire pour ses clients, tandis que Clonable n'est qu'une interface de balise, et elle modifie également le comportement de la méthode protégée à la main dans la superclasse. Il s'agit d'une utilisation extrêmement atypique de l'interface et n'est pas digne d'imitation.
2. La description Javadoc de la convention de la méthode des clones et de sa méthode de clone fragile est un peu ambiguë, comme suit: la convention Java SE8
La méthode Clone crée et renvoie une copie de l'objet. La signification exacte de la copie dépend de la classe de l'objet. Le sens général est que pour tout objet x, l'expression
x.clone() != x 为true x.clone().getClass() == x.getClass() renvoie également vrai, mais pas requis x.clone().equals(x)
Les deuxième et troisième expressions ci-dessus retournent facilement faux. Par conséquent, la seule chose qui peut garantir une vraie permanence est l'expression une, c'est-à-dire que les deux objets sont des objets indépendants.
3. Le domaine final de l'objet variable. Dans la méthode de clonage, si nous devons copier le domaine final de l'objet variable, il est en fait impossible de compiler et de passer en raison de restrictions finales. Par conséquent, afin d'implémenter le clonage, nous devons envisager d'abandonner le mot-clé final du domaine d'objet mutable.
4. Sécurité du thread Si vous décidez d'utiliser des classes de filetage pour implémenter l'interface clonable, vous devez vous assurer que sa méthode de clone est synchronisée. La méthode Object.clone par défaut n'est pas synchronisée.
En général, la méthode de clone en Java n'est pas réellement parfaite, il est donc recommandé d'éviter de l'utiliser autant que possible. Voici quelques alternatives.
Copier les constructeurs
La copie d'objets peut également être implémentée à l'aide d'un constructeur de copie.
1. Le constructeur de copie est également un type de constructeur
2. Un seul paramètre est accepté, le type de paramètre est la classe actuelle
3. Le but est de générer un nouvel objet avec les mêmes paramètres que
4. L'avantage de la méthode du constructeur de copie sur le clone est qu'il est simple et facile à implémenter.
Un exemple de code à l'aide d'un constructeur de copie
classe publique Car {roue de roue; Fabricant de cordes; voiture publique (roue de roue, fabricant de cordes) {this.wheel = roue; this. manufacturer = fabricant; } // Copie du constructeur public public (voiture de voiture) {this (car.wheel, car.manufufacturer); } Classe statique publique Wheel {String Brand; }}Notez que le code ci-dessus est implémenté comme une copie superficielle. Si vous souhaitez implémenter une copie profonde, veuillez vous référer au code suivant.
// Copie ConstructorPublic Car (voiture de voiture) {Wheel Wheel = new Wheel (); wheel.brand = car.wheel.brand; this.wheel = roue; this. manufacturer = car.Manufacturer;}Pour plus de commodité, nous pouvons également ajouter une méthode statique à la classe ci-dessus
Public Static Car NewInstance (Car Car) {Return New Car (Car);}Utiliser la série pour obtenir une copie profonde
En fait, une copie profonde des objets peut être réalisée en utilisant la sérialisation. Le bref code est le suivant
classe publique DeepCopyExample implémente Serialisable {private static final long SerialVersionUID = 6098694917984051357l; enfant public enfant; public DeepCopyExample Copy () {DeepCopyExample Copy = null; essayez {bytearrayoutputStream baos = new bytearrayoutputStream (); ObjectOutputStream OOS = new ObjectOutputStream (BAOS); oos.writeObject (this); BytearrayInputStream bais = new ByteArrayInputStream (baos.ToByteArray ()); ObjectInputStream oiS = new ObjectInputStream (bais); copy = (DeepCopyExample) ois.readObject (); } catch (ioException e) {e.printStackTrace (); } catch (classNotFoundException e) {e.printStackTrace (); } return copy; }}Parmi eux, l'enfant doit implémenter l'interface sérialisable
Classe publique Child implémente Serializable {private static final long SerialVersionUID = 6832122780722711261l; Nom de chaîne publique = ""; Public Child (String Name) {this.name = name; } @Override public String toString () {return "child [name =" + name + "]"; }}Utilisez des exemples et du code de test
Exemple DeepCopyExample = new DeepCopyExample (); example.child = new Child ("Exemple"); DeepCopyExample Copy = exampy.copy (); if (copy! = null) {copy.child.name = "copié"; System.out.println ("example.child =" + example.child + "; copy.child =" + copy.child);} // Résultat de sortie: Exemple.child = enfant [nom = exemple]; copy.child = enfant [name = copié]À en juger par les résultats de sortie, la modification de la valeur de l'enfant de l'objet Copy n'affecte pas la valeur de l'enfant de l'exemple de l'objet, c'est-à-dire en utilisant la sérialisation pour obtenir une copie profonde de l'objet.
Résumer
Ce qui précède est tout le contenu du clonage en Java. J'espère que cet article sera utile à tout le monde d'apprendre Java.