深拷贝与浅拷贝
在Java语言中(Python语言类似,可以导入copy包解决),我们对有时候对某一对象进行复制(克隆/拷贝)后发现,对其中某个对象的属性值进行了修改,与之相关的其它对象相应属性也随之变化。实际上,这是因为我们在进行对象克隆过程中,使用的是浅拷贝,也就是两个(或多个)对象引用指向同一个堆中对象实例。相反,深拷贝则是指拷贝的不同对象指向不同的堆中内存实例,因此深拷贝中对某一对象的修改不会影响其它的对象。
- 示例:首先构造两个简单的类,其中Student类中属性字段有Person类的引用,结构如下:
- Java代码展示浅拷贝和深拷贝(注释详细~)
public class CloneExample {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Person person = new Person(25, "小左");
Student student = new Student(person,80);
Student student2=null;
try {
student2=student.clone(); //深拷贝,需要实现Cloneable重写clone函数
} catch (CloneNotSupportedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(student.hashCode());
System.out.println(student2.hashCode()); //打印两个对象的hashcode,深拷贝两对象hashcode不同
student.setGrade(70);
student2.getPerson().setName("小邵");
System.out.println(student.getGrade()+"\t"+student2.getGrade());
System.out.println(student.getPerson().getName()+"\t"+student2.getPerson().getName());
Student student3=student2; //浅拷贝
System.out.println(student3.hashCode()); //两对象哈希值相同
}
}
class Person implements Cloneable{ //实现Cloneable接口,重写clone()函数(调用父类clone方法)实现深拷贝
private int age; //姓名
private String name; //年龄
public Person(int age,String name) {this.setAge(age);this.setName(name);}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
class Student implements Cloneable{ //实现Cloneable接口,重写clone()函数实现深拷贝
private Person person;
private int grade; //分数
public Student(Person person,int grade) {this.person=person;this.grade=grade;}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public Student clone() throws CloneNotSupportedException{
Student student=(Student)super.clone();
//注意类属性为非基本类型引用时需要调用该引用的clone函数重新赋值,否则默认该属性的引用为浅拷贝
student.setPerson(this.person.clone());
return student;
}
}
- 代码运行结果如下:
可以看到student与student2为深拷贝,两者hashcode不同,以及两对象属性值的相互修改互不影响,而student2与student3则为浅拷贝(直接引用赋值方式),因为它们的hashcode相同。 - 我们保持原有的代码不变,仅注释倒数第四行代码:
运行结果如下:
可以看到student和student2两个对象中的person却是指向同一堆中对象实例。这是因为我们在完全深拷贝时,没有调用person自身的clone()函数来给student2赋值,Java默认采用浅拷贝的方式将student的person浅拷贝给student2,导致我们在两个Student对象中修改person属性值,另一方都会受影响。因此,我们想要完全深拷贝某个对象,一定要在本层类重写clone()函数时调用上一层对象的clone()函数对该字段进行重新赋值(引用链有多长,我们重写clone()函数从底层开始,在本层类中调用上一层对象的clone()函数对该字段进行重新赋值)、
希望我的总结对你们有帮助,博客中相关问题欢迎询问探讨,相互学习。QQ:1476154032,以上。