java 实例对象拷贝,实例详解java对象拷贝

这篇文章主要介绍了java对象拷贝详解及实例的相关资料,需要的朋友可以参考下

java对象拷贝详解及实例

Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:@Test

public void testassign(){

Person p1=new Person();

p1.setAge(31);

p1.setName("Peter");

Person p2=p1;

System.out.println(p1==p2);//true

}

如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,而互不影响,就需要用到java中对象的复制,如原生的clone()方法。

如何进行对象克隆

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

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

② 覆盖clone()方法,可见性提升为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]

}

该测试用例只有两个基本类型的成员,测试达到目的了。

事情貌似没有这么简单,为Person增加一个Address类的成员:@Data

public class Address {

private String type;

private String value;

}

再来测试,问题来了。@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);

}

查看输出:false

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

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

遇到了点麻烦,只修改了p2的地址类型,两个地址类型都变成了Office。

浅拷贝和深拷贝

前面实例中是浅拷贝和深拷贝的典型用例。

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

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

也就是说,一个默认的clone()方法实现机制,仍然是赋值。

如果一个被复制的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝。

如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable接口并覆盖clone()方法。@Data

public class Address implements Cloneable {

private String type;

private String value;

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}

这样还不够,Person的clone()需要显式地clone其引用成员。@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;

}

}

重新跑前面的测试用例: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()方法。

与对象成员不同,继承关系中的clone不需要被复制类的clone()做多余的工作。

一句话来说,如果实现完整的深拷贝,需要被复制对象的继承链、引用链上的每一个对象都实现克隆机制。

前面的实例还可以接受,如果有N个对象成员,有M层继承关系,就会很麻烦。

利用序列化实现深拷贝

clone机制不是强类型的限制,比如实现了Cloneable并没有强制继承链上的对象也实现;也没有强制要求覆盖clone()方法。因此编码过程中比较容易忽略其中一个环节,对于复杂的项目排查就是困难了。

要寻找可靠的,简单的方法,序列化就是一种途径。

1.被复制对象的继承链、引用链上的每一个对象都实现java.io.Serializable接口。这个比较简单,不需要实现任何方法,serialVersionID的要求不强制,对深拷贝来说没毛病。

2.实现自己的deepClone方法,将this写入流,再读出来。俗称:冷冻-解冻。@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;

}

}

原型工厂类

为了便于测试,也节省篇幅,封装一个工厂类。

公平起见,避免某些工具库使用缓存机制,使用原型方式工厂。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拷贝对象

Dozer是一个Bean处理类库。

maven依赖

net.sf.dozer

dozer

5.5.1

测试用例:@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;

}

输出:p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))

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

注意:在万次测试中dozer有一个很严重的问题,如果DozerBeanMapper对象在for循环中创建,效率(dozer:7358)降低近10倍。由于DozerBeanMapper是线程安全的,所以不应该每次都创建新的实例。可以自带的单例工厂DozerBeanMapperSingletonWrapper来创建mapper,或集成到spring中。

还有更暴力的,创建一个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);

}

}

只要属性名相同,干~

继续蹂躏:@Data

public class People {

private String name;

private String age;

private Map 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);

}

}

利用Commons-BeanUtils复制对象

maven依赖

commons-beanutils

commons-beanutils

1.9.3

测试用例:@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();

}

}

}

利用cglib复制对象

maven依赖:

cglib

cglib

3.2.4

测试用例:@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);

}

结果大跌眼镜,cglib这么牛x,居然是浅拷贝。不过cglib提供了扩展能力:@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);

}

Orika复制对象

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

maven依赖:

ma.glasnost.orika

orika-core

1.5.0

测试用例:@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);

}

Spring BeanUtils复制对象

给Spring个面子,貌似它不支持深拷贝。Person p1=PersonFactory.newPrototypeInstance();

Person p2 = new Person();

Person p2 = (Person) BeanUtils.cloneBean(p1);

//BeanUtils.copyProperties(p2, p1);//这个更没戏

深拷贝性能对比@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

}

(10k)性能比较://dozer:721

//commons-beanutils:229

//cglib:133

//serializable:687

//orika:83

//clone:8

深拷贝总结

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 344 反转字符串

原题链接 解题思路&#xff1a;双指针首位交换&#xff0c;两个指针重合遍历交换完成 class Solution { public:void reverseString(vector<char>& s) {if(s.empty())return;int left0;int rights.size()-1;while(left<right){swap(s[left],s[right]);left;righ…

javaone_JavaOne 2015:高级模块化开发

javaoneJavaOne 2015看到了Project Jigsaw团队关于Java 9中的模块化的一系列讨论 。它们都是非常有趣的&#xff0c;并且充满了宝贵的信息&#xff0c;我敦促每个Java开发人员都注意它们。 除此之外&#xff0c;我想给社区一种搜索和引用它们的方法&#xff0c;因此我在这里总…

php劫持代码,利用php来嗅探劫持服务器数据

前几天刺在我们的maillist发了一个老外写的文章&#xff0c;大意是可以用php来实现数据的劫持和转发。我瞄了一下&#xff0c;确实可行&#xff0c;于是今天抽出了以前用来扯淡的时间&#xff0c;写了段代码验证了一下想法。老外的原文是一个PDF&#xff0c;有兴趣看的可以看看…

LeetCode 371 两个整数之和

原题链接 解题思路&#xff1a;位运算 class Solution { public:int getSum(int a, int b) {while(b){auto c((unsigned int) a & b)<<1;aa^b;bc;}return a;} };

spark rest_Spark简介,您的下一个REST Java框架

spark rest我希望您今年Java来了&#xff01; 今天&#xff0c;我们将研究一个清新&#xff0c;简单&#xff0c;美观且实用的框架&#xff0c;以Java编写REST应用程序。 它将非常简单&#xff0c;甚至根本不会看起来像Java。 我们将研究Spark Web框架。 不&#xff0c;它与Ap…

php求完数,php算法:求完全数 | 学步园

完全数&#xff1a;如果一个数的所有因数(不包括本身)的和刚好等于这个数自身&#xff0c;那么这个数就叫完全数求完全数function get_mul($num){for($i1;$i<$num;$i){for($j1;$j{//内层for循环求一个数的除自身外的所有因数if($i%$j0){$arr[]$j;}}if(isset($arr)){if(array…

oracle 授权 增删改查权限_Oracle增删改查与函数

SQL -- 结构化查询语言 关系型数据库分类&#xff1a; DDL DML DCL DQL TCL Oracle 的数据类型&#xff1a;字符 char() varchar2()数字 number(p,s)时间 date timestamp 文件 clob blob 二维表 table 创建表 CREATE create table 表名 ( 列名 数据类型 [约束], 列名 类型 ... …

LeetCode 1021 删除最外层的括号

原题链接 class Solution { public:string removeOuterParentheses(string S) {string str;int flag 0;for(char c: S){if(c( && flag>0){strc;}if(c ) && flag-->1){strc;}}return str;} };

javafx树视图加选框_JavaFX缺少的功能调查:表视图

javafx树视图加选框JavaFX的TableView&#xff08;和TreeTableView&#xff09;赢得了我最近的“ JavaFX缺失功能”调查以及许多后续讨论中&#xff08;尤其是我们苏黎世JavaFX Meetup小组的成员 &#xff09;中提到最多的控件的价格。 &#xff09;。 我想原因之一是一个简单的…

_用WSL,MobaXterm,Cmder配置linux开发环境

离不开Windows的理由很多,作为后端开发需要使用linux的情况也很多,双系统总归是不方便,而且linux下的GUI体验也没用Win 10好. 如果使用虚拟机,那么文件交换和网络等各种问题也需要解决,对系统的内存要求也更高一些.微软为了让更多的开发人员留在Win10上面,开发了WSL功能.目前的…

wamp测试php,php开发环境搭建/测试/LAMP/WAMP

常用的php开发环境由四部分构成:php预处理器&#xff0c;Apache服务器&#xff0c;mysql数据库&#xff0c;phpmyadmin(管理mysql的图形化页面界面)php语言是一种跨平台语言&#xff0c;常用php环境基于windows和Linux两种操作系统搭建&#xff0c;在Linux系统由Apache、mysql、…

LeetCode 933.最近的请求次数

原题链接 解题思路&#xff1a;主要保留PING中比3000大的 class RecentCounter { public:RecentCounter() {}int ping(int t) {time.push(t);while(time.front()<t-3000){ //查询T是否比3000大time.pop(); //T比3000小就出队}return time.size();}queue<int> time;…

对象容器设计模式_容器对象模式。 一种新的测试模式。

对象容器设计模式如果您搜索什么是页面对象的描述&#xff0c;您会发现页面对象模式为我们提供了一种以可重用和可维护的方式对内容建模的常识方法。 还要指出&#xff1a;在Web应用程序的UI中&#xff0c;您的测试与某些区域交互。 Page Object只是将它们建模为测试代码中的对…

php中上传图片怎么显示出来,PHP上传图片类显示缩略图功能

有缩略图功能 但是 感觉不全面&#xff0c;而且有点问题&#xff0c;继续学习&#xff0c;将来以后修改下/*** Created by PhpStorm.* User: Administrator* Date: 2016/6/28* Time: 21:04*/class upload{protected $fileMine;//文件上传类型protected $filepath;//文件上传路径…

LeetCode 1047. 删除字符串中的所有相邻重复项

原题链接 解题思路&#xff1a; 我们可以用栈来维护没有重复项的字母序列&#xff1a; 若当前的字母和栈顶的字母相同&#xff0c;则弹出栈顶的字母&#xff1b; 若当前的字母和栈顶的字母不同&#xff0c;则放入当前的字母。 class Solution { public:string removeDuplica…

javaparser_JavaParser入门:以编程方式分析Java代码

javaparser我最喜欢的事情之一是解析代码并对其执行自动操作。 因此&#xff0c;我开始为JavaParser做出贡献&#xff0c;并创建了两个相关项目&#xff1a; java-symbol-solver和Effectivejava 。 作为JavaParser的贡献者&#xff0c;我反复阅读了一些有关从Java源代码提取信…

prepare的用法 php,PHP中的操作mysqli的预处理prepare

这篇文章主要介绍了关于PHP中的操作mysqli的预处理prepare &#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下PHP中的操作mysqli的预处理prepare1、【PHP错误】Cannot pass parameter 2 by reference这个错误的意思是不能按引用传…

wps xml转换表格_这功能WPS卖近百元?教你免费将PDF转成Word

[PConline 应用]PDF文件如何转换成为Word&#xff1f;很多朋友研究这个问题已经很久了&#xff0c;PDF更利于统一格式传播&#xff0c;Word更便于编辑&#xff0c;因此收到PDF文件后、想要修改时要如何将PDF转换成Word可谓是一个刚需。当然&#xff0c;不少办公软件提供了这样的…

junit 预期错误_谨慎使用JUnit的预期异常

junit 预期错误有时&#xff0c;当我们收到对jOOQ或其他库的拉取请求时&#xff0c;人们会将单元测试中的代码更改为更“惯用的JUnit”。 特别是&#xff0c;这意味着他们倾向于更改此代码&#xff08;公认的不是那么漂亮的代码&#xff09;&#xff1a; Test public void tes…

LeetCode 231. 2的幂

原题链接 class Solution { public:bool isPowerOfTwo(int n) {if(n<0)return false;if((n&n-1) 0) return true;return false;} };