开发过程中,我们会遇到把现有的一个对象的所有成员拷贝给另一个对象的需求,会出现在组合、包含等关系中,比如学生和课程的关系
java存在基本数据类型和引用数据类型,在赋值操作、用作方法参数或返回值时,会有值传递和引用地址传递的差别。
浅拷贝
- 基本数据类型:值传递,其中一个对象修改值不会影响另外一个
- 引用数据类型:引用传递,拷贝的是内存地址,两个对象指向同一空间,其中一个对象修改值必然会影响到另外一个
如图:
代码示例如下:
课程类
/**
* 课程类
*
* @author wangkai
* @created 2020/3/19
*/
public class Subject {
private String name;
public Subject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Subject{" + this.hashCode() +
",name='" + name + '\'' +
'}';
}
}
学生类
/**
* 学生类
*
* @author wangkai
* @created 2020/3/19
*/
public class Student implements Cloneable {
private Integer age;
private String name;
private Subject subject;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
@Override
public String toString() {
return "Student{" + this.hashCode() +
",age=" + age +
", name='" + name + '\'' +
", subject=" + subject +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类
/**
* 浅拷贝
*
* @author wangkai
* @created 2020/3/19
*/
public class ShallowAndDeepCopy {
public static void main(String[] args) throws CloneNotSupportedException {
//初始化课程为语文课
Subject subject = new Subject("Chinese");
//学生1选课
Student student1 = new Student();
student1.setAge(20);
student1.setName("WangKai");
student1.setSubject(subject);
Student student2 = (Student) student1.clone();
//对象拷贝 Student student2 = student1;
student2.setAge(30);
student2.setName("XiaoMing");
//学生2学习了历史课
Subject subject2 = student2.getSubject();
subject2.setName("history");
System.out.println("Student1:" + student1.toString());
System.out.println("Student2:" + student2.toString());
}
}
运行结果:
Student1:Student{1639705018,age=20, name='WangKai', subject=Subject{1627674070,name='history'}}
Student2:Student{1360875712,age=30, name='XiaoMing', subject=Subject{1627674070,name='history'}}
Process finished with exit code 0
根据运行结果,可知student1.clone()克隆之后生成了新的对象student2,基础数据类型的修改互相不影响,引用数据类型的修改会有影响,两个对象生成的Subject引用类型指向的是同一个地址
引申:浅拷贝和对象拷贝的区别
将Student student2 = (Student) student1.clone()改为Student student2 = student1就成了对象拷贝
输出结果
Student1:Student{1639705018,age=30, name='XiaoMing', subject=Subject{1627674070,name='history'}}
Student2:Student{1639705018,age=30, name='XiaoMing', subject=Subject{1627674070,name='history'}}
Process finished with exit code 0
通过查看hashcode发现,对象拷贝没有生成新的Student对象,二者对象地址是一致的,而浅拷贝地址是不一样的
由于浅拷贝存在的问题,我们引出深拷贝
扫描二维码关注公众号,回复:
10276222 查看本文章
深拷贝
- 基础数据类型:和浅拷贝一致
- 引用数据类型:为引用数据类型新开一个独立内存空间,实现真正内容的拷贝
如图:
代码示例:
课程类:
/**
* 课程类
* 改造点:实现Cloneable,重新方法
*
* @author wangkai
* @created 2020/3/19
*/
public class Subject implements Cloneable {
private String name;
public Subject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Subject{" + this.hashCode() +
",name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
学生类:
/**
* 学生类
* 改动点:重新clone方法,对引用类型变量重新克隆赋值
* @author wangkai
* @created 2020/3/19
*/
public class Student implements Cloneable {
private Integer age;
private String name;
private Subject subject;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
@Override
public String toString() {
return "Student{" + this.hashCode() +
",age=" + age +
", name='" + name + '\'' +
", subject=" + subject +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.subject = (Subject) subject.clone();
return student;
}
}
测试类
/**
* 深拷贝:main方法与浅拷贝一致,改的是对象内部实现
*
* @author wangkai
* @created 2020/3/19
*/
public class ShallowAndDeepCopy {
public static void main(String[] args) throws CloneNotSupportedException {
//初始化课程为语文课
Subject subject = new Subject("Chinese");
//学生1选课
Student student1 = new Student();
student1.setAge(20);
student1.setName("WangKai");
student1.setSubject(subject);
Student student2 = (Student) student1.clone();
//Student student2=student1;
student2.setAge(30);
student2.setName("XiaoMing");
//学生2学习了历史课
Subject subject2 = student2.getSubject();
subject2.setName("history");
System.out.println("Student1:" + student1.toString());
System.out.println("Student2:" + student2.toString());
}
}
运行结果如下:
Student1:Student{1639705018,age=20, name='WangKai', subject=Subject{1627674070,name='Chinese'}}
Student2:Student{1360875712,age=30, name='XiaoMing', subject=Subject{1625635731,name='history'}}
Process finished with exit code 0
由输出结果可知:深拷贝之后,其中一个对象的数据类型和引用类型的修改都不会影响另外一个对象
注意:
- 对于有多层对象的,每个对象都需要实现Cloneable并重写clone()方法,进而实现了对象的串行拷贝
- 深拷贝相比于浅拷贝速度较慢并且花销较大
- 如果某个属性被transient修饰,那么该属性就无法被拷贝了
总结
- 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝方式
- 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的内容,并复制其内容