浅拷贝、深拷贝
1、首先了解
在学习浅拷贝和深拷贝前,我们需要先了解什么是引用类型和基本类型。
1.1、引用类型和基本类型
1、引用类型:引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象。
常见的引用类型:类类型,接口类型、数组(eg:char[])、String类
2、基本类型:基本类型的变量保存原始值,所以变量就是数据本身。
常见的基本类型:byte、short、int、long、char、float、double、Boolean。
1.2、值传递和引用传递
值传递: 在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下。
注意:String、BigInteger、BigDecimal以及包装器类型:Integer、Long、Short、Byte、Character、Boolean、Float和Double等不可变类都视为值传递。
引用传递: 引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
2、浅拷贝
理解:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝。
2.1、浅拷贝方法
2.1.1实现 Cloneable 接口
对需要拷贝的对象类实现 Cloneable 接口,如果对象中存在对其他类的引用,也需要实现cloneable接口。
示例:
@Data
class Person implements Cloneable{
private String name;
private Integer age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
问题: 拷贝对象中存在对其他对象的引用,则其他对象是拷贝的首地址值,相当于引用传递,源对象和拷贝对象对引用对象修改,都会修改统一数据源。
3、深拷贝
理解:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容。
3.1、深拷贝方法
3.1.1、方法一:实现 Cloneable 接口并重写clone方法
@Data
class User implements Cloneable{
private String name;
private Dog dog;
@Override
protected Object clone() throws CloneNotSupportedException {
User u=(User) super.clone();
u.dog=(Dog) dog.clone();
return u;
}
}
可实现深拷贝,但每个拷贝对象都需要重写clone方法,代码相当繁琐。
3.1.2、方法二:实现Serializable接口,通过对象的序列化和反序列化实现克隆
private static <T>T CloneObj(T obj){
T retobj=null;
try {
//写入流中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
//从流中读取
ObjectInputStream ios = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
retobj=(T) ios.readObject();
}catch (Exception e) {
e.printStackTrace();
}
return retobj;
}
高级写法
@SneakyThrows
@Deprecated
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@Cleanup ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(t);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
@Cleanup ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
}