
目录
1.Cloneable接口
1.1Cloneable接口中什么方法都没有,那为什么要继承该接口?
1.2.Cloneable接口什么方法都没有,那我该重写谁的clone()方法来实现对象拷贝?
1.3.实现对象拷贝的方法步骤
1.3.1让Person类实现Cloneadble接口
1.3.2重写Object的clone()方法
1.3.3注意抛异常和和向下转型
1.4.代码实现
2.深拷贝
3.浅拷贝
如何发生浅拷贝?
将浅拷贝变成深拷贝的思路
解答:在Cloneable接口的源码中,里面什么方法都没有,但是implements该接口的类则证明该类是可以被克隆的。
Cloneable接口源码
解答:所有类都默认继承Object类,所以需要重写Object类中的clone()方法就能实现对象的拷贝了。
我以自定义Person类举例
class Person implements Cloneable{
public int age;
public int id;
public Person(int age, int id) {
this.age = age;
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", id=" + id +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person(10,0001);
//1.由于重写的clone方法会抛出编译时异常,要么try-catch,要么继续抛
//2.clone()方法返回值为Object类型,因此需要向下转型
Person person2 = (Person)person1.clone();
System.out.println(person1);
System.out.println(person2);
}
}
结果:
图解代码:
简单理解:我改变person1对象中的属性值,不会影响到person2对象中的属性值,那么就叫做深拷贝。
从上图中可知,如果我修改person1对象的age属性为200,id属性也为200,应该是不会影响person2对象的属性值的。
直接看代码:
结果:可以发现,改变了person1对象中的成员变量的值,并不会影响person2中成员变量的值。
简单理解:我改变person1对象中的属性值,会影响到person2对象中的属性值,那么就叫做深拷贝。
看到这块基本上也就理解了Cloneable接口、深浅拷贝了。
接下来会说一说,如何发生浅拷贝,以及如何将浅拷贝转化为深拷贝。
---------------------------------------------------------------------------------------------------------------------------------
如下图所示,如果给我的Person类当中再添加一个引用类型的成员变量 ,那此时在调用clone()方法时,拷贝的副本(person2所指的对象)会将原对象当中的所有值全部拷贝,此时副本中的引用类型变量也存储的是0x999的地址,那么也就会让副本中的引用类型变量和原对象的引用类型变量指向同一片空间。如果指向了同一片空间,修改person1对象中引用类型的变量值就会让person2的变量值也会发生改变,那是不是就发生了浅拷贝!!!
代码演示:再自定义一个Money类,在Person类中定义一个Money类型的变量。
给person1.money.salary = 12,person2.money.salary = 200
结果:结果最终都是200,这次是浅拷贝了,但我们希望他们两个对象互不影响(也就是深拷贝)
方法一:
1.我们可以先将引用类型(Money)所指向的对象拷贝一份
2.接着我们在拷贝Person类的对象
3.让Person类对象的引用变量指向最初拷贝的那个(Money)对象
画个图解释一下
核心代码的实现:
结果:
源代码:
class Money implements Cloneable{
public int salary;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public int age;
public int id;
public Money money = new Money();
public Person(int age, int id) {
this.age = age;
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", id=" + id +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person(10,0001);
person1.money.salary = 12;
//1.先拷贝money对象
Money temp = (Money)person1.money.clone();
//2.在拷贝person2对象
Person person2 = (Person)person1.clone();
//3.person2.money指向temp
person2.money = temp;
person2.money.salary = 200;
System.out.println(person1.money.salary);
System.out.println(person2.money.salary);
}
}
方法二:
方法一是在main方法中进行修改的,我们希望调用完clone()方法就能做到深拷贝,因此需要修改Person类中的clone方法
图解:
核心代码块:
结果:两者已经互不影响了
源代码:
class Money implements Cloneable{
public int salary;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
public int age;
public int id;
public Money money = new Money();
public Person(int age, int id) {
this.age = age;
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", id=" + id +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
//先克隆一个Person对象副本
Person personTemp = (Person)super.clone();
//2.在克隆money对象的副本,并将副本的money指向克隆出来的money对象
personTemp.money = (Money)this.money.clone();
//3.返回该Person类对象的引用
return personTemp;
}
}
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1 = new Person(10,0001);
person1.money.salary = 12;
Person person2 = (Person)person1.clone();
person2.money.salary = 200;
System.out.println(person1.money.salary);
System.out.println(person2.money.salary);
}
}