很多优秀的同学已经了解浅拷贝和深拷贝的区别了,这边文章主要是实现深拷贝为主,虽然这类文章也很多,但是这个就是当做自己学习路上的一个脚印。写的不好,希望大佬勿喷。
1、深拷贝和浅拷贝的区别
深拷贝和浅拷贝的主要区别在于引用类型的对象的拷贝,浅拷贝只会拷贝引用对象的地址,原对象和拷贝对象实际上指向的都是同一个引用类型的地址。所以拷贝对象修改这个引用类型的对象时,原对象也会跟着改变。
浅拷贝的拷贝结果:

深拷贝就是拷贝的同时,引用类型的对象,也会跟着一起拷贝,拷贝对象和引用对象,完全不相关。
深拷贝的结果:

* 注意: 此处有一个点需要记住:String类型,String类型本身虽然也是引用类型,但是由于String类型底层是final类型的,同时因为有字符串常量池的存在,每次修改String类型的字段,实际上都会生成一个新的字符串常量,因此String类型虽然是引用类型,但是无须单独考虑
2、如何实现一个深拷贝
Java里面,浅拷贝的实现,实现Cloneable接口,然后将clone方法重写,并将修饰符由protected改为public,方便外部调用。
我们这边主要讲深拷贝,深拷贝一般有两种实现方式:
- 1、实现Cloneable接口,然后重写clone方法,在每个引用对象处,再使用一次clone的拷贝方法;
- 2、使用序列化的方式,将原对象转换为流,再将流转换成需要的对象。
下面分别会分别对两种拷贝方式进行实现:
//@Data
@Setter
@Getter
public class BaseDeepObject implement Cloneable{
private int id;
private String name;
public BaseDeepObject(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//@Data
@Setter
@Getter
public class DeepCloneObject implements Cloneable {
private Integer id;
private String name;
private BaseDeepObject baseDeepObject;
public DeepCloneObject(Integer id, String name, BaseDeepObject baseDeepObject) {
this.id = id;
this.name = name;
this.baseDeepObject = baseDeepObject;
}
/**
* 深拷贝
*
* @return
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
DeepCloneObject deepCloneObject = (DeepCloneObject) super.clone();
deepCloneObject.baseDeepObject = (BaseDeepObject) baseDeepObject.clone();
return deepCloneObject;
}
}
打印的结果:
BaseDeepObject baseDeepObject = new BaseDeepObject(1111, "引用对象");
DeepCloneObject deepCloneObject =new DeepCloneObject(12345, "深拷贝原对象", baseDeepObject);
try {
DeepCloneObject cloneObject = (DeepCloneObject) deepCloneObject.clone();
System.out.println(deepCloneObject == cloneObject);
System.out.println(deepCloneObject.equals(cloneObject));
System.out.println("原对象hash值" + deepCloneObject.hashCode());
System.out.println("真正的内存地址" + System.identityHashCode(deepCloneObject));
System.out.println("克隆对象的hash值" + cloneObject.hashCode());
System.out.println("拷贝对象真正的内存地址" + System.identityHashCode(cloneObject));
System.out.println("原对象里面的引用对象hash值" + deepCloneObject.getBaseDeepObject().hashCode());
System.out.println("克隆对象里面的引用对象hash值" + cloneObject.getBaseDeepObject().hashCode());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
false
false
原对象hash值1376790572
真正的内存地址1376790572
克隆对象的hash值1404601430
拷贝对象真正的内存地址1404601430
原对象里面的引用对象hash值1095240931
克隆对象里面的引用对象hash值1819274917
因此可以看出,深拷贝之后,引用对象的地址也已经改变,源对象和拷贝对象完全是独立的存在了。
此处肯定有小伙伴看到了,我不仅仅打印出来了hashCode,同时也取了真正的内存地址,这个主要是使用lombok的@Data注解导致的,@Data注解里面包含了@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode
这5个注解,会对hashCode和equals进行重写,因此取到的hashCode是有可能相同,但是 == 判断的时候,是false,equal判断的时候是true。感兴趣的小伙伴可以自己尝试一下@Data注解带来的比较。
*** 注:== 判断比较的内存地址,equal先比较hashCode,再比较每个字段的值
方式二:
public DeepCloneObject deepClone() {
DeepCloneObject newObject = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
// 使用输出流,将对象信息写入到流里面,做持久化
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeObject(this);
outputStream.flush();
// 从流里面,把信息读取出来,并重新赋值给一个新的对象
ByteArrayInputStream bin = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(bin);
newObject = (DeepCloneObject) objectInputStream.readObject();
outputStream.close();
objectInputStream.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return newObject;
}
BaseDeepObject baseDeepObject = new BaseDeepObject(1111, "引用对象");
DeepCloneObject deepCloneObject =
new DeepCloneObject(12345, "深拷贝原对象", baseDeepObject);
DeepCloneObject cloneObject = deepCloneObject.deepClone();
System.out.println(deepCloneObject == cloneObject);
System.out.println(deepCloneObject.equals(cloneObject));
System.out.println("原对象hash值" + deepCloneObject.hashCode());
System.out.println("真正的内存地址" + System.identityHashCode(deepCloneObject));
System.out.println("克隆对象的hash值" + cloneObject.hashCode());
System.out.println("拷贝对象真正的内存地址" + System.identityHashCode(cloneObject));
System.out.println("原对象里面的引用对象hash值" + deepCloneObject.getBaseDeepObject().hashCode());
System.out.println("克隆对象里面的引用对象hash值" + cloneObject.getBaseDeepObject().hashCode());
false
false
原对象hash值407323310
真正的内存地址407323310
克隆对象的hash值2069577154
拷贝对象真正的内存地址2069577154
原对象里面的引用对象hash值511928860
克隆对象里面的引用对象hash值88867683
方法二主要是把对象做序列化处理,将数据放到流里面做持久化,然后再读出来,这样的话,肯定是一个新的对象。
深拷贝和浅拷贝暂时就介绍到这边,感谢各位小伙伴的观看,有问题的话,可以在留言区留言,大家相互学习。