Maison > Java > javaDidacticiel > le corps du texte

Exemple détaillé de copie d'objet Java

Y2J
Libérer: 2017-05-02 14:15:00
original
1976 Les gens l'ont consulté

Cet article présente principalement l'explication détaillée et des exemples de copie d'objet Java. Les amis qui en ont besoin peuvent se référer à

Explication détaillée et exemples de copie d'objet Java

Java Assignment copie une référence d'objet. Si nous voulons obtenir une copie d'un objet, l'utilisation de l'opération d'affectation ne peut pas atteindre l'objectif :

@Test
public void testassign(){
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=p1;
 System.out.println(p1==p2);//true
}
Copier après la connexion

Si vous créez une nouvelle copie de l'objet, c'est-à-dire disons, leur état initial est exactement le même, mais vous pouvez modifier leurs états respectifs à l'avenir sans s'affecter mutuellement, vous devez donc utiliser la copie des objets en Java, comme la méthode native clone().

Comment cloner un objet

L'objet Object a une méthode clone(), qui réalise la copie de chaque attribut de l'objet, mais sa plage visible est protégée , donc La condition préalable à l'utilisation du clonage pour les classes d'entités est :

① Implémentez l'interface Cloneable, qui est une interface de marqueur et n'a pas de méthodes propres.

② Remplacez la méthode clone() et augmentez la visibilité auprès du public.

@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Test
public void testShallowCopy() throws Exception{
 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false
 p2.setName("Jacky");
 System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
 System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}
Copier après la connexion

Ce cas de test n'a que deux types de membres de base, et le test atteint son objectif.

Les choses ne semblent pas si simples. Ajoutez un membre de la classe Address à Person :

@Data
public class Address {
  private String type;
  private String value;
}
Copier après la connexion

Testez à nouveau, et le problème survient.

@Test
public void testShallowCopy() throws Exception{
 Address address=new Address();
 address.setType("Home");
 address.setValue("北京");

 Person p1=new Person();
 p1.setAge(31);
 p1.setName("Peter");
 p1.setAddress(address);

 Person p2=(Person) p1.clone();
 System.out.println(p1==p2);//false

 p2.getAddress().setType("Office");
 System.out.println("p1="+p1);
 System.out.println("p2="+p2);
}
Copier après la connexion

Voir le résultat :

false
p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
Copier après la connexion

J'ai rencontré des problèmes. J'ai seulement modifié le type d'adresse de p2, et les deux types d'adresse sont devenus Office.

Copie superficielle et copie profonde

Les exemples précédents sont des cas d'utilisation typiques de copie superficielle et de copie profonde.

Copie superficielle : Tous les attributs de valeur de l'objet copié contiennent les mêmes valeurs que celles de l'objet d'origine, et tous les attributs de référence d'objet pointent toujours vers l'objet d'origine.

Copie profonde : Sur la base d'une copie superficielle, toutes les variables qui font référence à d'autres objets sont également clonées et pointent vers le nouvel objet copié.

En d'autres termes, un mécanisme d'implémentation de méthode clone() par défaut est toujours assigné.

Si un attribut copié est un type de base, il vous suffit alors d'implémenter le mécanisme clonable de la classe actuelle. Il s'agit d'une copie superficielle.

Si les propriétés de l'objet copié contiennent des références à d'autres objets de classe d'entité, alors ces objets de classe d'entité doivent implémenter l'interface clonable et remplacer la méthode clone().

@Data
public class Address implements Cloneable {
  private String type;
  private String value;

  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}
Copier après la connexion

Cela ne suffit pas, Person's clone() doit cloner explicitement ses membres de référence.

@Data
public class Person implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj=super.clone();
    Address a=((Person)obj).getAddress();
    ((Person)obj).setAddress((Address) a.clone());
    return obj;
  }
}
Copier après la connexion

Réexécutez le scénario de test précédent :

false
p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
Copier après la connexion

Résumé de la copie profonde dans la méthode de clonage

① S'il y a un non- membre natif, Si vous définissez un membre d'un objet personnalisé, vous avez besoin de :

  1. Le membre implémente l'interface Cloneable et remplace la méthode clone(). N'oubliez pas de le promouvoir en. visibilité publique.

  2. En même temps, modifiez la méthode clone() de la classe copiée et ajoutez une logique de clonage de membre.

② Si ​​l'objet copié n'hérite pas directement de Object, il existe d'autres niveaux d'héritage entre les deux. Chaque super classe doit implémenter l'interface Cloneable et remplacer la méthode clone().

Contrairement aux membres d'objet, le clonage dans une relation d'héritage ne nécessite pas de travail redondant de la part de clone() de la classe copiée.

En un mot, si vous implémentez une copie profonde complète, chaque objet de la chaîne d'héritage et de la chaîne de référence de l'objet copié doit implémenter le mécanisme de clonage.

L'exemple précédent est acceptable, mais s'il y a N membres d'objet et des relations d'héritage de niveau M, ce sera très gênant.

Utiliser la sérialisation pour implémenter la copie profonde

Le mécanisme de clonage n'est pas limité par des types forts. Par exemple, l'implémentation de Cloneable ne force pas les objets sur la chaîne d'héritage. à également implémenter ; il n'y a aucune force. Nécessite de remplacer la méthode clone(). Par conséquent, il est facile de négliger l’un des liens pendant le processus de codage, ce qui rend difficile le dépannage de projets complexes.

Lorsque vous recherchez des méthodes fiables et simples, la sérialisation est la voie à suivre.

1. Chaque objet de la chaîne d'héritage et de la chaîne de référence de l'objet copié implémente l'interface java.io.Serializing. Ceci est relativement simple et ne nécessite l’implémentation d’aucune méthode. L’exigence SerialVersionID n’est pas obligatoire et convient à la copie approfondie.

2. Implémentez votre propre méthode deepClone, écrivez-la dans le flux, puis lisez-la. Communément appelé : gel-dégel.

@Data
public class Person implements Serializable {
  private String name;
  private Integer age;
  private Address address;
  public Person deepClone() {
    Person p2=null;
    Person p1=this;
    PipedOutputStream out=new PipedOutputStream();
    PipedInputStream in=new PipedInputStream();
    try {
      in.connect(out);
    } catch (IOException e) {
      e.printStackTrace();
    }

    try(ObjectOutputStream bo=new ObjectOutputStream(out);
        ObjectInputStream bi=new ObjectInputStream(in);) {
      bo.writeObject(p1);
      p2=(Person) bi.readObject();

    } catch (Exception e) {
      e.printStackTrace();
    }
    return p2;
  }
}
Copier après la connexion

Classe d'usine prototype

Afin de faciliter les tests et d'économiser de l'espace, une classe d'usine est encapsulée.

Par souci d'équité, évitez d'utiliser des mécanismes de mise en cache dans certaines bibliothèques d'outils et utilisez des usines de prototypes.

public class PersonFactory{
  public static Person newPrototypeInstance(){
    Address address = new Address();
    address.setType("Home");
    address.setValue("北京");

    Person p1 = new Person();
    p1.setAddress(address);
    p1.setAge(31);
    p1.setName("Peter");
    return p1;
  }
}
Copier après la connexion

Utilisez Dozer pour copier des objets

Dozer est une bibliothèque de traitement Bean.

dépendance maven

<dependency>
 <groupId>net.sf.dozer</groupId>
 <artifactId>dozer</artifactId>
 <version>5.5.1</version>
</dependency>
Copier après la connexion

Cas de test :

@Data
public class Person {
  private String name;
  private Integer age;
  private Address address;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    Person p2 = mapper.map(p1, Person.class);
    p2.getAddress().setType("Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}

@Data
public class Address {
  private String type;
  private String value;
}
Copier après la connexion

Sortie :

p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
Copier après la connexion

Remarque : il y a un bulldozer sur 10 000 tests A problème très sérieux. Si l'objet DozerBeanMapper est créé dans une boucle for, l'efficacité (dozer:7358) sera réduite de près de 10 fois. Étant donné que DozerBeanMapper est thread-safe, une nouvelle instance ne doit pas être créée à chaque fois. Vous pouvez utiliser l'usine singleton intégrée DozerBeanMapperSingletonWrapper pour créer un mappeur ou l'intégrer dans Spring.

Il y a encore plus de violence, créez une classe People :

@Data
public class People {
  private String name;
  private String age;//这里已经不是Integer了
  private Address address;

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    People p2 = mapper.map(p1, People.class);
    p2.getAddress().setType("Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}
Copier après la connexion

Tant que les noms d'attributs sont les mêmes, faites-le~

Continuez à ravager :

@Data
public class People {
  private String name;
  private String age;
  private Map<String,String> address;//��

  @Test
  public void testDozer() {
  Person p1=PersonFactory.newPrototypeInstance();
    Mapper mapper = new DozerBeanMapper();
    People p2 = mapper.map(p1, People.class);
    p2.getAddress().put("type", "Office");
    System.out.println("p1=" + p1);
    System.out.println("p2=" + p2);
  }
}
Copier après la connexion

Utilisez Commons-BeanUtils pour copier des objets

Dépendance Maven

<dependency>
 <groupId>commons-beanutils</groupId>
 <artifactId>commons-beanutils</artifactId>
 <version>1.9.3</version>
</dependency>
Copier après la connexion

Cas de test :

@Data
public class Person {
  private String name;
  private String age;
  private Address address;

  @Test
  public void testCommonsBeanUtils(){
  Person p1=PersonFactory.newPrototypeInstance();
    try {
      Person p2=(Person) BeanUtils.cloneBean(p1);
      System.out.println("p1=" + p1);
      p2.getAddress().setType("Office");
      System.out.println("p2=" + p2);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
Copier après la connexion

Utilisez cglib pour copier des objets

Dépendance Maven :

<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.2.4</version>
</dependency>
Copier après la connexion

Cas de test :

@Test
public void testCglib(){
 Person p1=PersonFactory.newPrototypeInstance();
 BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, false);
 Person p2=new Person();
 beanCopier.copy(p1, p2,null);
 p2.getAddress().setType("Office");
 System.out.println("p1=" + p1);
 System.out.println("p2=" + p2);
}
Copier après la connexion

Le résultat est choquant, cglib est tellement génial , il s'agit en fait d'une copie superficielle. Cependant, cglib offre des capacités d'extension :

@Test
public void testCglib(){
 Person p1=PersonFactory.newPrototypeInstance();
 BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, true);
 Person p2=new Person();
 beanCopier.copy(p1, p2, new Converter(){
  @Override
  public Object convert(Object value, Class target, Object context) {
   if(target.isSynthetic()){
    BeanCopier.create(target, target, true).copy(value, value, this);
   }
   return value;
  }
 });
 p2.getAddress().setType("Office");
 System.out.println("p1=" + p1);
 System.out.println("p2=" + p2);
}
Copier après la connexion

Orika复制对象

orika的作用不仅仅在于处理bean拷贝,更擅长各种类型之间的转换。

maven依赖:

<dependency>
 <groupId>ma.glasnost.orika</groupId>
 <artifactId>orika-core</artifactId>
 <version>1.5.0</version>
</dependency>
</dependencies>
Copier après la connexion

测试用例:

@Test
public void testOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Person p1=PersonFactory.newPrototypeInstance();
 Person p2 = mapper.map(p1, Person.class);
 System.out.println("p1=" + p1);
 p2.getAddress().setType("Office");
 System.out.println("p2=" + p2);
}
Copier après la connexion

Spring BeanUtils复制对象

给Spring个面子,貌似它不支持深拷贝。

Person p1=PersonFactory.newPrototypeInstance();
Person p2 = new Person();
Person p2 = (Person) BeanUtils.cloneBean(p1);
//BeanUtils.copyProperties(p2, p1);//这个更没戏
Copier après la connexion

深拷贝性能对比

@Test
public void testBatchDozer(){
 Long start=System.currentTimeMillis();
 Mapper mapper = new DozerBeanMapper();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("dozer:"+(System.currentTimeMillis()-start));
 //dozer:721
}
@Test
public void testBatchBeanUtils(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) BeanUtils.cloneBean(p1);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 System.out.println("commons-beanutils:"+(System.currentTimeMillis()-start));
 //commons-beanutils:229
}
@Test
public void testBatchCglib(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  BeanCopier beanCopier=BeanCopier.create(Person.class, Person.class, true);
  Person p2=new Person();
  beanCopier.copy(p1, p2, new Converter(){
   @Override
   public Object convert(Object value, Class target, Object context) {
    if(target.isSynthetic()){
     BeanCopier.create(target, target, true).copy(value, value, this);
    }
    return value;
   }
  });
 }
 System.out.println("cglib:"+(System.currentTimeMillis()-start));
 //cglib:133
}
@Test
public void testBatchSerial(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2=p1.deepClone();
 }
 System.out.println("serializable:"+(System.currentTimeMillis()-start));
 //serializable:687
}
@Test
public void testBatchOrika() {
 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

 mapperFactory.classMap(Person.class, Person.class)
 .field("name", "name")
 .byDefault()
 .register();
 ConverterFactory converterFactory = mapperFactory.getConverterFactory();
 MapperFacade mapper = mapperFactory.getMapperFacade();

 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  Person p2 = mapper.map(p1, Person.class);
 }
 System.out.println("orika:"+(System.currentTimeMillis()-start));
 //orika:83
}

@Test
public void testBatchClone(){
 Long start=System.currentTimeMillis();
 for(int i=0;i<10000;i++){
  Person p1=PersonFactory.newPrototypeInstance();
  try {
   Person p2=(Person) p1.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
 }
 System.out.println("clone:"+(System.currentTimeMillis()-start));
 //clone:8
}
Copier après la connexion

(10k)性能比较:

//dozer:721
//commons-beanutils:229
//cglib:133
//serializable:687
//orika:83
//clone:8
Copier après la connexion

深拷贝总结

原生的clone效率无疑是最高的,用脚趾头都能想到。

偶尔用一次,用哪个都问题都不大。

一般性能要求稍高的应用场景,cglib和orika完全可以接受。

另外一个考虑的因素,如果项目已经引入了某个依赖,就用那个依赖来做吧,没必要再引入一个第三方依赖。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!