Maison >Java >javaDidacticiel >Explication détaillée du mécanisme de copie Java

Explication détaillée du mécanisme de copie Java

黄舟
黄舟original
2017-03-01 11:45:561311parcourir


En Java, la copie est divisée en deux types : la copie approfondie et la copie superficielle. Java implémente une méthode appelée clone dans la superclasse publique Object. Le nouvel objet cloné par cette méthode est une copie superficielle et la méthode de clonage définie par vous-même est une copie complète.

(1) Méthode Clone dans Object

Si nous créons un nouvel objet, utilisons une instruction pour le référencer, puis utilisons une autre instruction pour référencer la déclaration précédente , alors le résultat final est : les deux variables déclarées pointeront vers le même objet, et si un endroit est modifié, tous seront modifiés. Si nous voulons créer une copie d'un objet qui est exactement la même que les différentes propriétés de l'objet et que la modification de la copie n'a rien à voir avec l'objet d'origine, nous devons alors utiliser la méthode clone à ce stade.

package Clone;import java.util.Date;/**
 * 
 * @author QuinnNorris 
 * java中的两种拷贝机制
 */public class Clone {

    /**
     * @param args
     * @throws CloneNotSupportedException 
     */
    public static void main(String[] args) throws CloneNotSupportedException {        
    // TODO Auto-generated method stub

        ClassA valA = new ClassA(1, "old", new Date());        
        // 声明一个新的ClassA对象,我们不需要太关注ClassA的功能
        ClassA valB = valA;        
        // 将valA引用的对象赋给valB
        valA.setObject("new");        
        // 更改valA中的值,此时valB也被更改了,因为valA和valB指向同一个对象

        valB = valA.clone();//通过clone方法制造副本
    }
}

La partie redéfinie de la méthode clone dans la classe ClassA :

//需要实现Cloneable接口public class ClassA implements Cloneable {

    public ClassA clone() throws CloneNotSupportedException {        
    return (ClassA) super.clone();
    //调用父类(Object)的clone方法
    }

}

1 Comment utiliser la méthode clone dans Object

<.> Quelqu'un a résumé les quatre règles d'utilisation de la méthode clone. Partageons-les ensemble :

  1. Afin d'obtenir une copie de l'objet, nous pouvons utiliser la fonction clone(). méthode de la classe Object.

  2. Remplacez la méthode clone() de la classe de base dans la classe dérivée et déclarez-la comme publique.

  3. Dans la méthode clone() de la classe dérivée, appelez super.clone().

  4. Implémentez l'interface Cloneable dans la classe dérivée.

2. Méthode de clonage modifiée protégée

Dans java.lang.Object, il définit la méthode de clonage sur Modification protégée, ceci est une situation très particulière. La portée de protected est la suivante : le package est visible et héritable. La raison de ce paramètre est que cette méthode renvoie un objet cloné, c'est-à-dire que le type de méthode de clonage est inconnu. Il n'existe aucun moyen de déterminer le type de valeur de retour. Naturellement, les générations futures ne peuvent que l'implémenter et la réécrire. , afin de permettre aux descendants d'hériter sans être trop ouvert, il est défini sur un type protégé.

3. Pour implémenter la méthode clone, vous devez implémenter l'interface Cloneable

Alors pourquoi devrions-nous implémenter l'interface Cloneable lorsque nous réécrivons la méthode clone ? En fait, l'interface Cloneable est une interface marquée en Java. Les interfaces marquées font référence à des interfaces sans méthodes ni attributs. Elles existent uniquement pour permettre à tout le monde de connaître certaines informations, et elles peuvent être jugées lors de l'utilisation de : xxx instanceof Cloneable. L'émergence de l'interface Cloneable vise à faire savoir aux concepteurs que le clonage est requis. Si un objet doit être cloné mais n'implémente pas (en fait, « implémentation » ici est remplacé par « écriture » est plus précis) l'interface Cloneable, une exception vérifiée sera générée.

4. Pour implémenter la méthode clone, vous devez appeler le clone de la classe parent

Afin de copier un objet qui est exactement le même que l'objet qui appelle la méthode, nous devons utiliser la classe parent La méthode clone d'une classe, et ainsi de suite pour la classe parent, jusqu'à ce que la méthode clone d'Object soit atteinte, alors à quoi sert la méthode clone d'Object ? Voici ce que dit l'API :

protected Object clone() lance CloneNotSupportedException Crée et renvoie une copie de cet objet.
La signification précise de « copie » peut dépendre de la classe de l’objet. Ce que cela fait, c'est que, pour tout objet x,
Expression :
x.clone() != x est vrai, Expression :
x.clone().getClass() == x.getClass() est également vrai, Mais ce ne sont pas des exigences à respecter.
Normalement :

x.clone().equals(x) est vrai, mais ce n'est pas une exigence qui doit être remplie.
Par convention, l'objet renvoyé doit être obtenu en appelant super.clone. Si une classe et toutes ses superclasses (sauf Object) respectent cette convention, alors
x.clone().getClass() == x.getClass().

Ce qui précède est une explication de base du clonage dans l'API. Ce que nous pouvons conclure, c'est que tant que spuer.clone() est appelé correctement, il renverra un objet cloné. Au moment de l'exécution, clone() dans Object identifie l'objet que vous souhaitez copier, puis alloue de l'espace pour cet objet, copie l'objet et copie le contenu de l'objet d'origine un par un dans l'espace de stockage du nouvel objet. Dans cet objet cloné, tous les attributs sont les mêmes que ceux de l'objet cloné, et ces mêmes attributs sont divisés en deux types :

Le premier : huit types primitifs et objets immuables (tels que String )

Deuxième type : Autres objets de classe

Pour le premier type, la méthode clone fixe leur valeur à la valeur de l'objet d'origine sans aucun problème. Pour le deuxième type, la méthode clone pointe simplement la référence du nouvel objet copié vers la référence pointée par l'objet d'origine. Le deuxième type d'objet classe sera modifié par les deux objets. Cette fois, il s’agit donc du concept de copies profondes et superficielles.

(2) Copie superficielle

Copie superficielle : Toutes les variables de l'objet copié contiennent les mêmes valeurs que l'objet d'origine, et toutes les références à d'autres objets Toujours pointe vers l'objet d'origine. En d’autres termes, une copie superficielle ne copie que l’objet en question, pas les objets auxquels il fait référence.

比如举个例子,一个类A中有另外一个类B类型的变量。在A重写clone函数调用super.clone的时候,创建的新对象和原来对象中的类B类型的变量是同一个,他们指向了同一个B的类型变量。如果在A中对B的变量做了修改,在新的拷贝出来的对象中B的变量也会被同样的修改。

请记住,直接调用super.clone实现的clone方法全部都是浅拷贝。

(三)深拷贝

深拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

通俗的说,如果说浅拷贝,开始的时候是两条线,如果在最后有一个其他类的变量,那么这两条线最后会合二为一,共同指向这变量,都能对他进行操作。深拷贝则是完完全全的两条线,互不干涉,因为他已经把所有的内部中的变量的对象全都复制一遍了。

深拷贝在代码中,需要在clone方法中多书写调用这个类中其他类的变量的clone函数。

(四)串行化深拷贝

在框架中,有的时候我们发现其中并没有重写clone方法,那么我们在需要拷贝一个对象的时候是如何去操作的呢?答案是我们经常会使用串行化方法,实现Serializable接口。

去寻找其他的方法来替代深拷贝也是无可奈何的事情,如果采用传统的深拷贝,难道你拷贝一个对象的时候向其中追无数层来拷贝完所有的对象变量么?先不谈这么做的时间消耗,仅仅是写这样的代码都会让人望而生畏。串行化深拷贝就是这样一个相对简单的方法。

把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。

上面是网上的专业解释,我也不在这里班门弄斧了。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。

public Object deepClone() 
{ 
 //写入对象 
 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); 
 ObjectOutputStream oo=new ObjectOutputStream(bo); 
 oo.writeObject(this); 
 //读取对象
 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); 
 ObjectInputStream oi=new ObjectInputStream(bi); 
 return(oi.readObject()); 
}

虽然这种学院派的代码看起来很复杂,其实只是把对象放到流里,再拿出来。相比较分析判断无数的clone,这样简直是再简单不过了。这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否设成transient。

transient:一个对象只要实现了Serilizable接口,这个对象就可以被序列化(序列化是指将java代码以字节序列的形式写出,即我们上面代码前三行写入对象),Java的这种序列化模式为开发者提供了很多便利,可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个的所有属性和方法都会自动序列化。但是有种情况是有些属性是不需要序列号的,所以就用到这个关键字。只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

(五)总结

在实际的应用中,深拷贝和浅拷贝只是两个概念,不一定谁比谁好,要按照实际的工作来确定如何去拷贝一个对象。如果在数据库操作方面,为了取出一张表时不涉及其他的表,肯定需要使用浅拷贝,而在框架的Serializable中,虽然耗时,但是深拷贝是非常有必要的。

在java中,拷贝分为深拷贝和浅拷贝两种。java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。

 以上就是java拷贝机制详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn