引用拷贝
定义:
引用拷贝只复制对象的地址值,不会创建新的对象,改变拷贝对象的属性,原对象属性也会发生变化
实现方式
通常是"="直接赋值,
浅拷贝:
定义
浅拷贝会创建新的对象接收,所以改变拷贝对象的属性时不会影响源对象,但是浅拷贝不会创建内部嵌套对象,而是引用嵌套对象地址,所以如果嵌套对象属性发生改变,都会发生改变。
(备注:如果有嵌套多层对象也是拷贝引用地址因为只需拷贝最外面的嵌套对象就拷贝了里面对象的地址)
实现方式
1.jdk:被拷贝的对象实现了 Cloneable 接口,并且重写了 clone() 方法
class Person implements Cloneable {
// ...变量属性
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 使用Object类的clone()方法进行浅拷贝
}
}
2.工具类库:
hutool的ObjectUtils.clone()方法即是浅克隆也是深克隆,如果类实现了Cloneable接口就是浅克隆,如果只实现了Serializable接口就是深克隆,原理是序列化和反序列化
hutool的 BeanUtil.copyProperties()方法也是浅拷贝
深拷贝:
定义
深拷贝会递归地复制对象及其内部的所有嵌套对象,创建一个完全独立的副本与原始对象之间完全独立,对其中一个的修改都不会影响另一个
实现方式
1.实现序列化接口,通过对象的序列化和反序列化可以实现深拷贝。或是通过第三方工具类库原理是一样的比如hutool的ObjectUtils.clone()方法即是浅克隆也是深克隆,如果类实现了Cloneable接口就是浅克隆,如果只实现了Serializable接口就是深克隆,代码见测试用例
public static <T> T clone(T obj) {
T result = ArrayUtil.clone(obj);
if (null == result) {
if (obj instanceof Cloneable) {
result = ReflectUtil.invoke(obj, "clone", new Object[0]);
} else {
result = cloneByStream(obj);
}
}
return result;
}
2.递归复制,不管用谁的克隆方法主要是要自己写递归的逻辑
测试用例
@Test
public void copyTest() throws CloneNotSupportedException {
CopyTest copyTest = new CopyTest();
copyTest.setTrader(new Trader("Bob","cd"));
CopyTest copyTest2 = (CopyTest)copyTest.clone();
copyTest2.getTrader().setName("newname");
log.info("源对象地址:{} 源嵌套对象地址:{}, {}",copyTest,copyTest.getTrader(), copyTest.getTrader().getName());
log.info("jdk.clone对象地址:{} 嵌套对象地址:{}, {}",copyTest2,copyTest2.getTrader(), copyTest2.getTrader().getName());
CopyTest copyTest3 = ObjectUtil.clone(copyTest);
log.info("ObjectUtil.clone对象地址:{} 嵌套对象地址:{}",copyTest3,copyTest3.getTrader());
CopyTest copyTest4 = new CopyTest();
DataConverterUtil.copyProperties(copyTest,copyTest4);
log.info("cglib.copy对象地址:{} 嵌套对象地址:{}",copyTest4,copyTest4.getTrader());
// 深拷贝
// 将对象序列化为字节流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(copyTest);
// 反序列化为新对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bis);
CopyTest copyTest5 = (CopyTest)in.readObject();
log.info("序列化反序列化对象地址:{} 嵌套对象地址:{}",copyTest5,copyTest5.getTrader());
}
打印结果:
浅拷贝:源对象地址:com.wrx.king.entity.CopyTest@309e345f 源嵌套对象地址:com.wrx.king.entity.Trader@56a6d5a6, newname
浅拷贝:jdk.clone对象地址:com.wrx.king.entity.CopyTest@71b1176b 嵌套对象地址:com.wrx.king.entity.Trader@56a6d5a6, newname
浅拷贝:ObjectUtil.clone对象地址:com.wrx.king.entity.CopyTest@4b14c583 嵌套对象地址:com.wrx.king.entity.Trader@56a6d5a6
浅拷贝:cglib.copy对象地址:com.wrx.king.entity.CopyTest@4ddced80 嵌套对象地址:com.wrx.king.entity.Trader@56a6d5a6
深拷贝:序列化反序列化对象地址:com.wrx.king.entity.CopyTest@79079097 嵌套对象地址:com.wrx.king.entity.Trader@4d1c00d0
总结jdk的clone方法和一般工具类的克隆方法都是浅拷贝,通过流序列化的原理就是深拷贝
深拷贝只能通过序列化和反序列化过程实现或者自己重写clone方法递归复制