Cloneable接口(浅拷贝和深拷贝的区别)

在这里插入图片描述

前言

Object类中存在这一个clone方法,调用这个方法可以创建一个对象的“拷贝”。但是想要合法调用clone方法,必须要先实现Clonable接口,否则就会抛出CloneNotSupportedException异常。

1 Cloneable接口

//Cloneable接口声明
public interface Cloneable {
	
}

我们发现Cloneable接口中没有任何定义字段和方法,也就是说Cloneable接口是个空接口,既然是一个空接口那么实现这个空接口的意思是什么呢?

Cloneable接口作为一个空接口,它的作用是用来标记当前实现类,表示当前类是可以被克隆的,也就是我们将Cloneable接口当作一个标记接口。

1.1 clone()

前面我们提到Object类中存在一个clone()方法,调用这个方法可以创建一个对象的“拷贝“,我们通过clone()方法将下文定义类克隆,观察出现的现象来学习Cloneable接口。

//Person.java
public class Person {
	//字段
	public String name;
	public int age;

	//方法
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void print() {
		System.out.println("我是" + this.name);
	}

	//Object类中的clone()方法被protected修饰只能在同个包或子类中使用
	//想在外界使用就必须重写
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

//Main.java
public class Main {
	//当方法中抛出受查异常,借助throws将异常抛给方法的调用者来处理
	public static void main(String[] args) throws CloneNotSupportedException {
		//实例一个Person对象
		Person person1 = new person("张三",18);
	} 
}
  • 当我们调用clone()克隆出张三的拷贝会出现什么现象?
Person person2 = (Person)person1.clone();

在这里插入图片描述

当我们在编译器运行时,编译器向我们抛出了受查异常CloneNotSupportedException,这说明当前类不支持克隆,也就是说当前类中没有实现Cloneable接口这个作为克隆标记的接口,当我们在Person类中实现这个接口后:
在这里插入图片描述

当程序运行时,我们就会将person1引用的对象中的内容拷贝一份到person2所引用的对象中去,堆栈关系图如下:
在这里插入图片描述

也就是说当我们要利用Object类中的clone()方法去克隆一个对象时,我们需要进行以下步骤:

  1. 重写父类Object中的克隆方法
  2. 注意对克隆返回对象进行类型转换,clone()方法默认放回Object
  3. 处理异常CloneNotSupportedException
  4. 实现Cloneable接口,标记当前接口可克隆

2. 深拷贝和浅拷贝

我们将上文提到的Person类进行修改,以修改后的person类进行深拷贝和浅拷贝的讲解

//Money.java
public class Money {
	public double m = 1999.9;
}

//Person.java
public class Person implements Cloneable{
	public String name;
	public Money money;

	//方法
	public Person(String name) {
		this.name = name;
		this.money = new Money(); 
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

2.1 浅拷贝

  • #浅拷贝: 仅复制对象本身及其引用的子对象的引用,而不复制这些子对象本身。结果是新对象和原对象共享子对象。如果子对象被修改,原对象也会受到影响。
Person person1 = new Person("张三");  
Person person2 = (Person)person1.clone();  
System.out.println("通过person1修改前:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);  
person1.money.m = 1888.8;  
System.out.println("通过person1修改后:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);

执行后:
在这里插入图片描述

也就是我们可以得到通过浅拷贝的堆栈图如下:
在这里插入图片描述

我们的子对象引用了一个对象,也就是说在浅拷贝过程中只实现了子对象引用的拷贝,所以我们通过person1修改的money对象中的m实际上修改的是同一个。

2.2 深拷贝

  • #深拷贝: 递归地复制对象及其所有子对象,创建一个完全独立的副本。新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。
    我们希望深拷贝是实现了所有子对象的拷贝,形成一个完全独立的副本,也就是我们希望得到以下堆栈图:
    在这里插入图片描述

那么如何通过堆栈图所述得到我们想要的深拷贝效果呢?我们可以通过修改重写的clone()方法和实现Money类的Cloneable接口来实现。

//Money.java
public class Money implements Cloneable{  
    public double m = 1999.9;  
  
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    }  
}
//Person.java
public class Person implements {
	//...
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Person tmp = (Person)super.clone();//(1)
		tmp.money = (Money)this.money.clone();//(2)
		return tmp;
	}
}

//(2)通过将子对象本身拷贝创建出独立的副本

需要注意的是:

  • 对子对象进行拷贝的时候需要相对应的子对象所对应类也实现Cloneable接口

实现深拷贝后再次执行代码:

Person person1 = new Person("张三");  
Person person2 = (Person)person1.clone();  
System.out.println("通过person1修改前:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);  
person1.money.m = 1888.8;  
System.out.println("通过person1修改后:");  
System.out.println(person1.money.m);  
System.out.println(person2.money.m);

执行结果:
在这里插入图片描述
深拷贝过程创建一个完全独立的副本,使得新对象与原对象及其子对象完全独立,修改新对象不会影响原对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值