文章目录

  • Java中的深浅拷贝方式
    • 实现Cloneable重写clone()方法
      • 如何进行对象克隆
      • 浅拷贝和深拷贝
        • 浅拷贝案例
        • 深拷贝案例
      • clone方式小结
    • 原型工厂类
    • 利用Dozer拷贝对象
    • 利用Commons-BeanUtils复制对象
    • Orika复制对象

Java中的深浅拷贝方式

实现Cloneable重写clone()方法

如何进行对象克隆

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

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

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

浅拷贝和深拷贝

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

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

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

浅拷贝案例

@Data
public class Person implements Cloneable {private String name;private Integer age;@Overridepublic Person clone(){try {return (Person)super.clone();} catch (CloneNotSupportedException e) {return new Person();}}
}

测试结果

    @Testpublic void test1() {Person p1 = new Person();p1.setAge(31);p1.setName("Peter");Person p2 = p1.clone();System.out.println(p1 == p2);//falsep2.setName("Jacky");System.out.println("p1=" + p1);//p1=Person [name=Peter, age=31]System.out.println("p2=" + p2);//p2=Person(name=Jacky, age=31)}

深拷贝案例

修改person类,添加address属性

@Data
public class Address {private String type;private String value;
}@Data
public class Person implements Cloneable {private String name;private Integer age;private Address address;@Overridepublic Person clone(){try {return (Person)super.clone();} catch (CloneNotSupportedException e) {return new Person();}}
}

测试结果

    @Testpublic void testShallowCopy(){Address address=new Address();address.setType("Home");address.setValue("北京");Person p1=new Person();p1.setAge(31);p1.setName("Peter");p1.setAddress(address);Person p2=p1.clone();System.out.println(p1==p2);//falsep2.getAddress().setType("Office");System.out.println("p1="+p1);//p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))System.out.println("p2="+p2);//p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))}

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

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

@Data
public class Address implements Cloneable{private String type;private String value;@Overridepublic Address clone(){try {return (Address) super.clone();} catch (CloneNotSupportedException e) {return new Address();}}
}@Data
public class Person implements Cloneable {private String name;private Integer age;private Address address;@Overridepublic Person clone(){try {Person person = (Person) super.clone();Address address = person.getAddress();person.setAddress(address.clone());return person;} catch (CloneNotSupportedException e) {return new Person();}}
}

重新跑前面的测试用例:

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

clone方式小结

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

    • 该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。
    • 同时,修改被复制类的clone()方法,增加成员的克隆逻辑
  • 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法

原型工厂类

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

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;}
}

利用Dozer拷贝对象

maven依赖

<dependency><groupId>net.sf.dozer</groupId><artifactId>dozer</artifactId><version>5.5.1</version>
</dependency>

测试用例:

   @Testpublic 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 Person{private String name;private Integer age;private Address address;
}@Data
public class Address {private String type;private String value;
}

输出结果:

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

利用Commons-BeanUtils复制对象

maven依赖

<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.3</version>
</dependency>

测试用例:

    @Testpublic void testCommonsBeanUtils(){Person p1=PersonFactory.newPrototypeInstance();try {Person p2=(Person) BeanUtils.cloneBean(p1);//p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))System.out.println("p1=" + p1);p2.getAddress().setType("Office");//p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))System.out.println("p2=" + p2);} catch (Exception e) {e.printStackTrace();}}

Orika复制对象

maven依赖

<dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.0</version>
</dependency>

测试用例

@Test
public void testOrika() {MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();mapperFactory.classMap(Person.class, Person.class).byDefault().register();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);
}p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))

Java中的深浅拷贝方式相关推荐

  1. Java中的深浅拷贝问题你清楚吗?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 一.前言 拷贝这个词想必大家都很熟悉,在工作中经常需要拷贝 ...

  2. 浅谈关于java中的深浅拷贝

    一.浅拷贝(shallow copy) 1.如何实现浅拷贝? Object类 是所有类的直接或间接父类,Object中存在clone方法,如下 protected native Object clon ...

  3. java中的深浅拷贝

    https://blog.csdn.net/QLH04_04/article/details/89849812 转载于:https://www.cnblogs.com/wangjing666/p/11 ...

  4. opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类

    opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类 参考文章: (1)opengl中的Floatbuffer和 ...

  5. java中的io复用_从 Java 中的零拷贝到五种IO模型

    在之前的文章中,我们聊过了 Java 中的零拷贝,零拷贝就是指数据不会在内核空间和用户空间之间相互拷贝.这样就减少了内核态与用户态的切换,自然就很高效. 拷贝文件只是 IO 操作中一个特殊的情况,大多 ...

  6. JS 和 Java 中URL特殊字符编码方式

    前几天遇到url特殊字符编码的问题,在这里整理一下: JavaScript 1.  编码 escape(String) 其中某些字符被替换成了十六进制的转义序列. 解码 unescape(String ...

  7. java中的异常处理代码,java_深入剖析Java中的各种异常处理方式,1. 调试追踪代码:public s - phpStudy...

    深入剖析Java中的各种异常处理方式 1. 调试追踪代码: public static void enterTryMethod() { System.out.println("enter a ...

  8. java数组初始化的方式,java中初始化数组的方式有几种

    java中初始化数组的方式有几种 发布时间:2020-06-01 16:12:45 来源:亿速云 阅读:153 作者:鸽子 三种初始化方式: 1.静态初始化:创建+赋值 2.动态初始化:先创建再赋值 ...

  9. java中常见跳出循环的方式总结

    java中常见跳出循环的方式一般有两种,一种是常用的break,continue,return方式:另一种是循环标记的方式. 方式一:break,continue,return方式 案例: break ...

  10. 关于Java中的Map遍历方式比较

    最近在看阿里巴巴最新版的Java开发手册,里面的内容还是很值得去阅读学习,下面是我对Java中Map的遍历方式的比较和总结: 第一种:使用entrySet()的形式来遍历,也是效率高,推荐使用的一种遍 ...

最新文章

  1. SpringBoot 学习 | raibaby halo 之安装部署 - Ali0th
  2. BZOJ 3261 最大异或和 可持久化Trie树
  3. Rise of Shadows 闰年leap year-无法线性筛
  4. 在桌面拔和平精英改成计算机,和平精英一键修改画质电脑版
  5. 如何关闭SAP Fiori的病毒扫描设置
  6. 基于 Kubernetes 的基础设施即代码
  7. GraalVM上的Picocli:极快的命令行应用程序
  8. 传感器绕着世界坐标系旋转产生的疑惑
  9. QQ偷偷删除图片被EMUI系统拦截?华为、腾讯双方回应...
  10. 纳什叫上林书豪,投了一家AI篮球训练公司
  11. [buaa-SE-2017]个人作业-Week1
  12. webform CustomValidator
  13. React Native 接入微博、微信、QQ 登录功能
  14. php startup memcache,centos php 安装memcache模块
  15. windows2003下卸载ie8
  16. IntelliJ IDEA 配置Jetty启动项目
  17. php图片留白,PHP:图片不变形处理(留白处理与截取处理)-奇乐网
  18. ffmpeg超时设置
  19. 鲍鱼数据集处理/matlab
  20. linux 文件夹的作用是什么,Linux个文件夹的作用及含义

热门文章

  1. eclipse安装tomcat时只有locahost,不显示server name
  2. ADDA数模转换(PCF8591)
  3. 名悦集团:冬季汽车开暖风有哪些注意事项
  4. 【论文笔记】DeiT论文阅读笔记
  5. DELPHI 旧控件安装到 DELPHI11 新版环境的操作
  6. 我安装archlinux的过程总结
  7. kaggle-2美国人口普查年收入50K分类
  8. 机器人操作系统ROS(9)Gazebo物理仿真(摄像头仿真)
  9. 职场新人注意事项:抖包袱可以,抖机灵不要
  10. C#中如何调出工具箱