1. 克隆与引用
1.2 引用
在了解深克隆与浅克隆的区别之前,需先了解克隆与引用的区别。
我们通常通过"="对非八大数据和String类型的类进行赋值操作,这就是引用
如:
public class Student{
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
再在测试类中进行测试,比较两个对象是否为同一个对象,也可以通过调试去查看地址是否相等
public class CloneTest {
public static void main(String[] args) {
Student s1 = new Student("Haku",16);
Student s2 = s1;
System.out.println(s1 == s2);
}
}
结果很明显是true(调试可以明显看出两个对象的内存地址是一样的),并且修对象s2的属性值时,s1的相同属性值也会发生改变
s2.setName("Sloth");
System.out.println(s1.getName() == s2.getName());
结果同样也为true,明明没有修改s1的属性值,s1的属性值却被修改了,而且当我们销毁s2对象时,s1也会被同时销毁,也就是内存泄露。为了避免这种情况,通常会在创建类时实现Cloneable接口来实现克隆。
1.2 克隆
在所有java的主类Object中有一个方法Clone()克隆,我们通过在Student类中对Clone()方法的重写来实现克隆,重写方法具体如下:
public class Student implements Cloneable{
@Override
public Object clone(){
Student student = null;
try {
student = (Student)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
}
修改下测试类
public class CloneTest {
public static void main(String[] args) {
Student s1 = new Student("Haku",16);
Student s2 = (Student) s1.clone();
System.out.println(s1 == s2);
}
}
结果显而易见的为false,两者不为同一个对象
1.2.1 Cloneable接口有何用
当我们点开Cloneable查看其代码时,会发现其实它是一个空接口,当我们去掉implements Cloneable是再运行上述代码时,会发现报错java.lang.CloneNotSupportedException: myclone.Student
我们再去jdk的api中查看Clone()方法发现
CloneNotSupportedException - 如果对象的类不支持Cloneable接口。 覆盖clone方法的子类也可以抛出此异常以指示实例无法克隆。
所以再进行克隆时一定要实现Cloneable接口,即便它是一个空接口
2 浅克隆与深克隆
2.1 浅克隆与深克隆的区别
- 浅克隆:对自定义类中没有有指向自定义类的克隆操作为浅克隆。对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String数据类型;但如果被克隆对象中包括含除八种数据类型和String类型外的其它数据的属性,浅克隆不会进行赋值而是拷贝。
- 深克隆:自定义类中有指向自定义类的克隆操作为深克隆。深克隆是在浅克隆的基础上,递归地克隆除8种基本数据类型和String类型外的属性,为这些属性重写分配内存而非引用。
2.2 浅克隆
我们再创建一个类Teacher 其中属性有(String name,int age,Student student)并实现Cloneable接口重写Clone()方法:
public class Teacher implements Cloneable{
private String name;
private int age;
private Student student;
public Teacher() {
super();
}
public Teacher(String name, int age, Student student) {
super();
this.name = name;
this.age = age;
this.student = student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
protected Object clone(){
Teacher teacher = null;
try {
teacher = (Teacher)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
}
再调用测试类主函数
public static void main(String[] args) {
Student s1 = new Student("Haku",16);
Teacher t1 = new Teacher("Sloth",28,s1);
Teacher t2 = (Teacher)t1.clone();
System.out.println(t1 == t2);
System.out.println(t1.getStudent() == t2.getStudent());
}
结果打印为:
false
true
Teacher的克隆成功了,但Teacher的属性student却没成功克隆,属于同一个对象,这就是浅克隆
那如何浅克隆修改为深克隆呢?
2.3 深克隆
我们修改一下Teacher的Clone()方法
@Override
protected Object clone(){
Teacher teacher = null;
try {
teacher = (Teacher)super.clone();
teacher.student = (Student)this.student.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
让student属性也被克隆
再调用测试类
结果为:
false
false
t1 和 t2 不是同一个对象,同时t1.student与t2.student也不是同一个对象这就是深克隆。
总结
- 引用:对象A在引用对象B时,当对象A的属性发生改变时对象B的属性也会同时发生改变,因为二者实际上为同一个对象。
- 浅克隆:对象A在克隆对象B时,当对象A的属性发生改变时对象B的属性不会同时发生改变;但若存在非八大类和Sting的数据类型的属性时,若在对象A中修改这些属性,则对象B也会发生改变。因为克隆的不完全,所以被称为浅克隆
- 深克隆:深克隆是在浅克隆的基础上进行了,改进,使得每个类型的属性都被完全克隆。
以上内容都是笔者对于深浅克隆与引用的一些拙见,如有不当,还请诸位斧正。