介绍
在开发过程中,经常会遇到将一个对象的所有成员拷贝给另一个对象的需求。
普通的对象形式一般如下:
定义一个普通Student类:
public class Student {
private String name;
private Integer age;
private Integer totalScore;
//省略get set
...
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", totalScore=" + totalScore +
", classs=" + classs +
'}';
}
}
public static void main(String[] args) {
Student stu = new Student();
stu.setName("张三");
stu.setAge(18);
Student stu1 = stu;
stu1.setName("李四");
sout(stu.equals(stu1));
sout(stu1.getName());
}
以上程序运行结果:
true
李四
由以上结果得出结论:普通对象拷贝没有生成新的对象,二者对象在内存中地址是一样的,这种情形下就会存在一个问题,当修改其中一个对象(这里其实应该叫引用)的值,另一个对象的值也会改变。内存模型图如下:
由上面的问题引出了浅拷贝与深拷贝,根据对对象的拷贝程度分为:
- 浅拷贝
- 深拷贝
浅拷贝
概念
浅拷贝会创建一个新的对象,这个对象是对原对象属性值的完整的拷贝,如果是基本类型拷贝的是基本类型,如果是引用,拷贝的是引用指向的对象的内存地址;所以当拷贝对象修改引用的属性时,原对象的引用的属性值也会发生改变
实现
要实现浅拷贝的类,需要实现Cloneable接口,覆盖clone()方法
public class Student implements Cloneable{
private String name;
private Integer age;
private Integer totalScore;
private Classs classs;
public Student() {
}
public Student(String name, Integer age, Integer totalScore, Classs classs) {
this.name = name;
this.age = age;
this.totalScore = totalScore;
this.classs = classs;
}
//省略get set方法
@Override
protected Object clone() {
//浅拷贝直接调用父类的clone方法
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", totalScore=" + totalScore +
", classs=" + classs +
'}';
}
}
public class Classs{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Classs() {
}
public Classs(String name) {
this.name = name;
}
@Override
public String toString() {
return "Classs{" +
"name='" + name + '\'' +
'}';
}
}
main方法测试并输出结果
public static void main(String[] args) throws CloneNotSupportedException {
Classs classs = new Classs("三年2班");
Student stu = new Student("小明",18,666,classs);
Student stu1 = (Student) stu.clone();
stu1.setName("小芳");
stu1.setAge(17);
stu1.setTotalScore(691);
Classs classs1 = stu.getClasss();
classs1.setName("四年1班");
System.out.println(student.toString());
System.out.println(student1.toString());
}
打印结果:
Student{name='小芳', age=18, totalScore=666, classs=Classs{name='四年1班'}}
Student{name='小明', age=17, totalScore=691, classs=Classs{name='四年1班'}}
由打印结果可知:当stu调用clone方法实现拷贝对象,再对拷贝对象普通属性进行重新赋值可以发现两个对象的属性值都不相同,而引用属性classs的name属性,在改变之后,两个对象的引用属性值都发生了改变。
深拷贝
概念
其实就是在浅拷贝的基础上,使得引用属性的类拷贝时也在内存中开辟对立的空间存放拷贝的引用属性对象,保证在修改拷贝对象或原对象的属性的前提下不会影响另一个。
实现
对于引用类型的成员变量,其类的内部也要需要实现 Cloneable
并重写 clone()
方法,如果有多层对象嵌套的,每个对象都需要实现 Cloneable
并重写 clone()
方法。
public class Student implements Cloneable{
private String name;
private Integer age;
private Integer totalScore;
private Classs classs;
public Student() {
}
public Student(String name, Integer age, Integer totalScore, Classs classs) {
this.name = name;
this.age = age;
this.totalScore = totalScore;
this.classs = classs;
}
//省略get set方法
@Override
protected Object clone() {
//深拷贝需要对引用属性创建新的对象并分配内存,
try {
Student student = (Student) super.clone();
student.classs = (Classs) classs.clone();
return student;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", totalScore=" + totalScore +
", classs=" + classs +
'}';
}
}
public class Classs implements Cloneable{
private String name;
//省略get set方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Classs{" +
"name='" + name + '\'' +
'}';
}
}
main方法测试
public static void main(String []args){
Classs classs = new Classs("三年2班");
Student student = new Student("小明",18,666,classs);
Student student1 = (Student) student.clone();
student1.setName("小芳");
student1.setAge(17);
student1.setTotalScore(691);
Classs classs1 = student.getClasss();
classs1.setName("四年1班");
System.out.println(student.toString());
System.out.println(student1.toString());
}
测试打印结果如下:
Student{name='小明', age=18, totalScore=666, classs=Classs{name='四年1班'}}
Student{name='小芳', age=17, totalScore=691, classs=Classs{name='三年2班'}}
由输出结果可知,两个对象之间的成员属性发生改变之后,并没有互相影响。
总结
深拷贝相较于浅拷贝,主要的是为引用数据类型创建了新的内存空间,
使两个不同的对象的引用数据指向了不同的内存地址,在改变其引用数据的属性时,不会引起连锁反应对另一个产生影响。