1.前言
首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对象或者引用数据来说在进行浅拷贝时,只是将对象的引用复制了一份,也就内存地址;即两个不同的变量指向了同一个内存地址,那么在改变任一个变量的值都是改变这个内存地址所指向的值,所以两个变量的值都会改变。
上方这句话不太好理解,这样,我把他们区别出来,做两个讲解,一个是关于基本类型的拷贝;一个关于引用数据类型的拷贝,分开理解,会大大降低学习成本~
2.基本类型的拷贝
在基本类型的拷贝中,深拷贝
和浅拷贝
的作用就没什么区别了,拷贝出来的对象都是拥有相同的数值,这里用语言文字很难描述,可以看下面的代码和图来帮助理解。
public static void main(String[] args) throws CloneNotSupportedException { //原来的对象 Person person1=new Person("张三",15); //克隆出来的对象 Person person2=(Person) person1.clone(); }
我们打印这两个对象的地址:
观察可以看出,他两个的地址是不相同的,也就是说,他两个现在是不同的对象。
他两个在堆、栈图中是如何表示的呢?看下方的图:
我们可以看出,p2
把p1
中的所有数据都重新生成了一份,让给让p2
指向新生成的那份数据。
3.引用类型的拷贝
这个就很有引用类型拷贝就会在深拷贝
和浅拷贝
上区别很大了!
我们仍然以人来举例子,人身上一般都会带着money,我们给人添加一个新的成员变量(钱),并且我们让钱这个成员变量改为引用变量。
3.1 关于引用类型的浅拷贝
class Money { public double value=99.99; } class Person implements Cloneable{ public String name; public int age; public Money money=new Money(); public Person(String name, int age) { this.name = name; this.age = age; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class testDemo2 { public static void main(String[] args) throws CloneNotSupportedException { //原来的对象 Person person1=new Person("张三",15); //克隆出来的对象 Person person2=(Person) person1.clone(); System.out.print("p1的钱钱数:"); System.out.println(person1.money.value); System.out.print("p2的钱钱数:"); System.out.println(person2.money.value); } }
打印出来的结果:
p1的钱钱数:99.99
p2的钱钱数:99.99
我们可以观察结果得出,p1的钱钱和p2的钱钱我们设置好的99.99。
当我们去尝试修改p1钱钱的数时:
public static void main(String[] args) throws CloneNotSupportedException { //原来的对象 Person person1=new Person("张三",15); //克隆出来的对象 Person person2=(Person) person1.clone(); System.out.println("修改p1的钱钱数"); person1.money.value=50; System.out.print("p1的钱钱数:"); System.out.println(person1.money.value); System.out.print("p2的钱钱数:"); System.out.println(person2.money.value); }
代码打印结果:
我去,你修改p1
的钱钱数,为啥我p2
的钱钱数也发送了改变,这不就相当于别人花的钱扣在了我身上?这可不是我想要的结果!
这到底怎能回事?其实很好理解,当我们在进行拷贝时,我们拷贝money这个成员变量时,我们拷贝的数值是一个引用变量,这个引用变量又指向一个对象。因此当我们修改p1的钱的数值时,我们时顺着引用变量找所指向的值将其修改了,又因为两个引用对象指向同一个值,因此修改后,再通过引用变量查看值,就是修改后的值了;上述过程就是浅拷贝了,它的结构图如下:
3.2 关于引用类型的深拷贝
如何解决这样的问题呢?其实很简单,当我们进行拷贝时,我们让引用变量引用的对象重新创建一个新的对象,并且让拷贝的那一份的引用变量指向创建的对象。
我们主要修改的就算Person
类中的clone
方法,我们先让Money
类实现Cloneable
接口,然后在Person
类中的clone
方法调用Money
类中的clone
方法。(这里比较绕,看不懂没关系,下面有画的结构图,图看起来会很好理解!)
class Money implements Cloneable{ public double value=99.99; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable{ public String name; public int age; public Money money=new Money(); public Person(String name, int age) { this.name = name; this.age = age; } @Override protected Object clone() throws CloneNotSupportedException { Person person=(Person) super.clone(); person.money=(Money) person.money.clone(); return person; } } public class testDemo2 { public static void main(String[] args) throws CloneNotSupportedException { //原来的对象 Person person1=new Person("张三",15); //克隆出来的对象 Person person2=(Person) person1.clone(); System.out.println("修改p1的钱钱数"); person1.money.value=50; System.out.print("p1的钱钱数:"); System.out.println(person1.money.value); System.out.print("p2的钱钱数:"); System.out.println(person2.money.value); } }
代码运行出来的结果:
可以看出,修改p1的钱数,不在应用p2的钱数了。
上方这个过程就是深拷贝,深拷贝的结构图如下: