Java中的深浅拷贝方式
文章目录
- 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中的深浅拷贝方式相关推荐
- Java中的深浅拷贝问题你清楚吗?
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 一.前言 拷贝这个词想必大家都很熟悉,在工作中经常需要拷贝 ...
- 浅谈关于java中的深浅拷贝
一.浅拷贝(shallow copy) 1.如何实现浅拷贝? Object类 是所有类的直接或间接父类,Object中存在clone方法,如下 protected native Object clon ...
- java中的深浅拷贝
https://blog.csdn.net/QLH04_04/article/details/89849812 转载于:https://www.cnblogs.com/wangjing666/p/11 ...
- opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类
opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类 参考文章: (1)opengl中的Floatbuffer和 ...
- java中的io复用_从 Java 中的零拷贝到五种IO模型
在之前的文章中,我们聊过了 Java 中的零拷贝,零拷贝就是指数据不会在内核空间和用户空间之间相互拷贝.这样就减少了内核态与用户态的切换,自然就很高效. 拷贝文件只是 IO 操作中一个特殊的情况,大多 ...
- JS 和 Java 中URL特殊字符编码方式
前几天遇到url特殊字符编码的问题,在这里整理一下: JavaScript 1. 编码 escape(String) 其中某些字符被替换成了十六进制的转义序列. 解码 unescape(String ...
- java中的异常处理代码,java_深入剖析Java中的各种异常处理方式,1. 调试追踪代码:public s - phpStudy...
深入剖析Java中的各种异常处理方式 1. 调试追踪代码: public static void enterTryMethod() { System.out.println("enter a ...
- java数组初始化的方式,java中初始化数组的方式有几种
java中初始化数组的方式有几种 发布时间:2020-06-01 16:12:45 来源:亿速云 阅读:153 作者:鸽子 三种初始化方式: 1.静态初始化:创建+赋值 2.动态初始化:先创建再赋值 ...
- java中常见跳出循环的方式总结
java中常见跳出循环的方式一般有两种,一种是常用的break,continue,return方式:另一种是循环标记的方式. 方式一:break,continue,return方式 案例: break ...
- 关于Java中的Map遍历方式比较
最近在看阿里巴巴最新版的Java开发手册,里面的内容还是很值得去阅读学习,下面是我对Java中Map的遍历方式的比较和总结: 第一种:使用entrySet()的形式来遍历,也是效率高,推荐使用的一种遍 ...
最新文章
- SpringBoot 学习 | raibaby halo 之安装部署 - Ali0th
- BZOJ 3261 最大异或和 可持久化Trie树
- Rise of Shadows 闰年leap year-无法线性筛
- 在桌面拔和平精英改成计算机,和平精英一键修改画质电脑版
- 如何关闭SAP Fiori的病毒扫描设置
- 基于 Kubernetes 的基础设施即代码
- GraalVM上的Picocli:极快的命令行应用程序
- 传感器绕着世界坐标系旋转产生的疑惑
- QQ偷偷删除图片被EMUI系统拦截?华为、腾讯双方回应...
- 纳什叫上林书豪,投了一家AI篮球训练公司
- [buaa-SE-2017]个人作业-Week1
- webform CustomValidator
- React Native 接入微博、微信、QQ 登录功能
- php startup memcache,centos php 安装memcache模块
- windows2003下卸载ie8
- IntelliJ IDEA 配置Jetty启动项目
- php图片留白,PHP:图片不变形处理(留白处理与截取处理)-奇乐网
- ffmpeg超时设置
- 鲍鱼数据集处理/matlab
- linux 文件夹的作用是什么,Linux个文件夹的作用及含义