1、BeanUtil本地简单测试
在项目中由于需要对某些对象进行深度拷贝然后进行持久化操作,想到了apache和spring都提供了BeanUtils的深度拷贝工具包,自己写了几个Demo做测试,定义了两个类User和Person,其中User的属性引用了Person类。

public class User {
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址 private Person person; //包装类 //get set方法此处省略
}
//Person类
public class Person {
private int id;
private String userName ;
private int age ;
private String mobilePhone ;
public Person(){}
public Person(int id,String userName, int age, String mobilePhone) {
this.id = id;
this.userName = userName;
this.age = age;
this.mobilePhone = mobilePhone;
} //get set方法此处省略
}

编写测试方法进行调研,主要是查看对象中包装的对象是否引用了同一个地址,从而判断是否是深度拷贝还是浅拷贝

    @Test
public void CopyTest(){
User user=new User();
user.setId(1);
user.setSex("man");
user.setUsername("Tison");
user.setAddress("address");
user.setBirthday(new Date());
Person p=new Person();
p.setUserName("p1");
user.setPerson(p);
User target=new User();
BeanUtils.copyProperties(user,target);
System.out.println(target.getAddress()==user.getAddress());
System.out.println(target.getPerson()==user.getPerson());
System.out.println(user.toString());
System.out.println(target.toString());
}

打印结果:

false  (String属性的内存地址不相等)
false (包装对象的内存地址不相等)
src.main.mybatis.User@7907ec20
src.main.mybatis.User@546a03af

两个对象的哈希码不相等,引用对象的地址也不相同,并且对包装对象的操作都是互不影响,简单测试下可以看到BeanUtils实现了深度拷贝的效果。

2、项目测试
但是到了本人所做的项目中,BeanUtils的效果就不是深度拷贝了,用伪代码进行简单说明:

//source为A对象,target为B对象
BeanUtils.copyProperties(source,target);
//调用setSecret方法将B对象的某型包装属性set为null
setSecret(target);
//分别打印对比的结果
System.out.println(target.getUserInfo()==source.getUserInfo());
System.out.println(source.getUserInfo().hashCode());
System.out.println(target.getUserInfo().hashCode());
//打印测试结果
true
1589531316
1589531316

两份对象里的包装对象内存地址比较结果为true,而且对象的哈希吗指向了同一位置。
显而易见,BeanUtils并未进行深度拷贝。本人在项目中正因为采用了BeanUtils的拷贝方法,在对两份对象的不同操作时都会互相影响导致持久化的异常,可见基于BeanUtils的拷贝方法并不是万能的,而且由于源码中采用反射机制,其性能也被许多博主诟病,在网上进行了综合调研,发现BeanUtils的copyProperties()方法的确存在着浅拷贝的情况,这对于持久化操作实体类的时候是很大的一个坑,那么最靠谱的深拷贝方法还是要序列化后写流的方法,只是该方法需要实现Serializable接口。

3、深拷贝
深复制(深克隆)被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被复制过的新对象,而不再试原有的那些被引用的对象,换言之,深复制把要复制的对象所引用的对象都复制了一遍。
把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做“解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。

在项目中我们需要克隆的对象可能包含多层引用类型,这就要涉及到多层克隆问题,多层克隆不仅要将克隆对象实现序列化接口,引用对象也同样的要实现序列化接口:

public class User implements Serializable{
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private Person person; //引用类型 public User myColon(){
User copy=null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
copy = (User) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return copy;
} //此处省略get-set方法代码
}

引用类型也需要实现Serializable接口,否则会序列化失败。

public class Person implements Serializable {
private int id;
private String userName ;
private int age ;
private String mobilePhone ;
public Person(){}
public Person(int id,String userName, int age, String mobilePhone) {
this.id = id;
this.userName = userName;
this.age = age;
this.mobilePhone = mobilePhone;
}
//此处省略get-set方法
}

结论:

结论:
1、BeanUtils的copyProperties()方法并不是完全的深度克隆,在包含有引用类型的对象拷贝上就可能会出现引用对象指向同一个的情况,且该方法的性能低下,项目中一定要谨慎使用。
2、要实现高性能且安全的深度克隆方法还是实现Serializable接口,多层克隆时,引用类型均要实现Serializable接口。

 

最新文章

  1. Codeforces 731C Socks 并查集
  2. Mysql 拼接字段查询语句和join查询拼接和时间查询
  3. 网络通信分享(一):数字签名,数字证书,https通信,数据加密
  4. 图像分割之(三)从Graph Cut到Grab Cut
  5. oracle和sql server的区别(1)
  6. HTTPS Everywhere – 保障隐私和信息安全的利器
  7. mime type 概要介绍
  8. BZOJ3404: [Usaco2009 Open]Cow Digit Game又见数字游戏
  9. ArgumentError: Error #1063: BasicChart/dataFunc() 的参数数量不匹配。应该有 2 个,当前为 3 个。
  10. cf919D 线性dp+拓扑排序
  11. RTMP HLS HTTP 直播协议一次看个够
  12. CSS学习笔记-01-2D转换模块
  13. REST easy with kbmMW #17 – Database 6 – Existing databases
  14. Python tuple
  15. VUE 使用之:nextTick
  16. AcceptAsync和BeginAccept的区别
  17. angular 模板语法(官方文档摘录)
  18. 学习 swift (1)
  19. HttpMessageConverter
  20. System类及其getProterties( )和getProperty( )

热门文章

  1. NOI2014魔法森林题解报告
  2. 代码收藏系列--javascript--日期函数
  3. Codeforces 585E. Present for Vitalik the Philatelist(容斥)
  4. Codeforces Round #207 (Div. 2)A B C E 水 思路 set 恶心分类
  5. webstorm 激活破解方法大全
  6. phpstorm改变文件编码由utf变为gbk
  7. 效应量Effect Size
  8. 新生代Eden与两个Survivor区的解释
  9. java中BigDecimal在金融行业中的使用
  10. Spring REST 异常处理