Java中的深浅拷贝方式

x33g5p2x  于2022-06-27 转载在 Java  
字(5.1k)|赞(0)|评价(0)|浏览(668)

Java中的深浅拷贝方式

实现Cloneable重写clone()方法

如何进行对象克隆

Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:

① 实现Cloneable接口,这是一个标记接口,自身没有方法。

② 覆盖clone()方法,可见性提升为public。

浅拷贝和深拷贝

浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象

深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象

也就是说,一个默认的clone()方法实现机制,仍然是赋值,如果一个呗赋值的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝,如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable接口并覆盖clone()方法

浅拷贝案例
  1. @Data
  2. public class Person implements Cloneable {
  3. private String name;
  4. private Integer age;
  5. @Override
  6. public Person clone(){
  7. try {
  8. return (Person)super.clone();
  9. } catch (CloneNotSupportedException e) {
  10. return new Person();
  11. }
  12. }
  13. }

测试结果

  1. @Test
  2. public void test1() {
  3. Person p1 = new Person();
  4. p1.setAge(31);
  5. p1.setName("Peter");
  6. Person p2 = p1.clone();
  7. System.out.println(p1 == p2);//false
  8. p2.setName("Jacky");
  9. System.out.println("p1=" + p1);//p1=Person [name=Peter, age=31]
  10. System.out.println("p2=" + p2);//p2=Person(name=Jacky, age=31)
  11. }
深拷贝案例

修改person类,添加address属性

  1. @Data
  2. public class Address {
  3. private String type;
  4. private String value;
  5. }
  6. @Data
  7. public class Person implements Cloneable {
  8. private String name;
  9. private Integer age;
  10. private Address address;
  11. @Override
  12. public Person clone(){
  13. try {
  14. return (Person)super.clone();
  15. } catch (CloneNotSupportedException e) {
  16. return new Person();
  17. }
  18. }
  19. }

测试结果

  1. @Test
  2. public void testShallowCopy(){
  3. Address address=new Address();
  4. address.setType("Home");
  5. address.setValue("北京");
  6. Person p1=new Person();
  7. p1.setAge(31);
  8. p1.setName("Peter");
  9. p1.setAddress(address);
  10. Person p2=p1.clone();
  11. System.out.println(p1==p2);//false
  12. p2.getAddress().setType("Office");
  13. System.out.println("p1="+p1);//p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
  14. System.out.println("p2="+p2);//p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
  15. }

这里我们可以发现只修改p2的address,可是结果显示p1和p2的address都进行了修改

这里如果要保证只修改p2的address,需要address实体类也实现Cloneable接口和重写clone方法,并且Person的clone()需要显式地clone其引用成员

  1. @Data
  2. public class Address implements Cloneable{
  3. private String type;
  4. private String value;
  5. @Override
  6. public Address clone(){
  7. try {
  8. return (Address) super.clone();
  9. } catch (CloneNotSupportedException e) {
  10. return new Address();
  11. }
  12. }
  13. }
  14. @Data
  15. public class Person implements Cloneable {
  16. private String name;
  17. private Integer age;
  18. private Address address;
  19. @Override
  20. public Person clone(){
  21. try {
  22. Person person = (Person) super.clone();
  23. Address address = person.getAddress();
  24. person.setAddress(address.clone());
  25. return person;
  26. } catch (CloneNotSupportedException e) {
  27. return new Person();
  28. }
  29. }
  30. }

重新跑前面的测试用例:

  1. false
  2. p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
  3. p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

clone方式小结

  • 如果有一个非原生成员,如自定义对象的成员,那么就需要:

  • 该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。

  • 同时,修改被复制类的clone()方法,增加成员的克隆逻辑

  • 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法

原型工厂类

为了方便测试,节省篇幅,封装一个工程类

  1. public class PersonFactory{
  2. public static Person newPrototypeInstance(){
  3. Address address = new Address();
  4. address.setType("Home");
  5. address.setValue("北京");
  6. Person p1 = new Person();
  7. p1.setAddress(address);
  8. p1.setAge(31);
  9. p1.setName("Peter");
  10. return p1;
  11. }
  12. }

利用Dozer拷贝对象

maven依赖

  1. <dependency>
  2. <groupId>net.sf.dozer</groupId>
  3. <artifactId>dozer</artifactId>
  4. <version>5.5.1</version>
  5. </dependency>

测试用例:

  1. @Test
  2. public void testDozer() {
  3. Person p1= PersonFactory.newPrototypeInstance();
  4. Mapper mapper = new DozerBeanMapper();
  5. Person p2 = mapper.map(p1, Person.class);
  6. p2.getAddress().setType("Office");
  7. System.out.println("p1=" + p1);
  8. System.out.println("p2=" + p2);
  9. }
  10. @Data
  11. public class Person{
  12. private String name;
  13. private Integer age;
  14. private Address address;
  15. }
  16. @Data
  17. public class Address {
  18. private String type;
  19. private String value;
  20. }

输出结果:

  1. p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
  2. p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

利用Commons-BeanUtils复制对象

maven依赖

  1. <dependency>
  2. <groupId>commons-beanutils</groupId>
  3. <artifactId>commons-beanutils</artifactId>
  4. <version>1.9.3</version>
  5. </dependency>

测试用例:

  1. @Test
  2. public void testCommonsBeanUtils(){
  3. Person p1=PersonFactory.newPrototypeInstance();
  4. try {
  5. Person p2=(Person) BeanUtils.cloneBean(p1);
  6. //p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
  7. System.out.println("p1=" + p1);
  8. p2.getAddress().setType("Office");
  9. //p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
  10. System.out.println("p2=" + p2);
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }

Orika复制对象

maven依赖

  1. <dependency>
  2. <groupId>ma.glasnost.orika</groupId>
  3. <artifactId>orika-core</artifactId>
  4. <version>1.5.0</version>
  5. </dependency>

测试用例

  1. @Test
  2. public void testOrika() {
  3. MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
  4. mapperFactory.classMap(Person.class, Person.class)
  5. .byDefault()
  6. .register();
  7. MapperFacade mapper = mapperFactory.getMapperFacade();
  8. Person p1=PersonFactory.newPrototypeInstance();
  9. Person p2 = mapper.map(p1, Person.class);
  10. System.out.println("p1=" + p1);
  11. p2.getAddress().setType("Office");
  12. System.out.println("p2=" + p2);
  13. }
  14. p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
  15. p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

相关文章

最新文章

更多