您现在的位置是:首页 > 文章详情

java提高(15)---java深浅拷贝

日期:2019-03-13点击:447

java提高(15)---java深浅拷贝

一、前言

为什么会有深浅拷贝这个概念?

我觉得主要跟JVM内存分配有关,对于基本数据类型,只存在栈内存,所以它的拷贝不存在深浅拷贝这个概念。而对于对象而言,一个对象的创建会在内存中分配两块空间,一个在栈内存存对象的引用指针,一个在堆内存存放对象。这个时候会有一个问题,你拷贝的只是这个引用指针还是拷贝两块内存一起拷贝,这个时候就会有深浅拷贝一说。
还有之前我认为Arrays.copyOf()是深度拷贝,亲测后发现原来它也是浅拷贝。下面进行具体说明。


二、数据类型

数据分为基本数据类型(int, boolean, double, byte, char等)和对象数据类型。
基本数据类型的特点:直接存储在栈(stack)中的数据.
引用数据类型的特点:在栈内存存储对象引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

1090617-20190313235427505-245278508.jpg


三、什么是浅拷贝和深拷贝

首先需要明白,深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。那先来看看浅拷贝和深拷贝的概念。
在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 =号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

浅拷贝:如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象。

深拷贝:在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量。

深拷贝和浅拷贝的示意图大致如下:
1090617-20190313235500918-1556393479.jpg

1090617-20190313235507983-512560464.jpg

具体接下来代码演示。


四、代码演示

1、浅拷贝

Person

public class Person { public String name; public Integer age; public String sex; /** * 提供get和set方法和全参构造函数 */ }

Test

 public static void main(String[] args) throws Exception { Person person = new Person("小小",3,"女"); //将person值赋值给person1 Person person1 = person; System.out.println(person); System.out.println(person1); person1.setName("小小她爸"); System.out.println("person 中 name为:"+person.getName()); System.out.println("person1 中 name为:"+person.getName()); }

查看运行结果
1090617-20190313235604444-1064708140.jpg

从图片中我们可以很明显看出,它们指向的内存地址是一致的,同样我改变person1的属性值时发现person的属性值也改变了。

说明:对于对象用"=" 赋值 其实只是引用指针的复制,这两个引用还是指向同一个对象。

2、深拷贝

如果要实现深拷贝就会比较复杂点

Student

/** * 如果对象要实现深拷贝 那么实体需要做两步 * 1、实体实现Cloneable接口 * 2、重写 clone()方法 */ public class Student implements Cloneable { public String name; public Integer age; public String sex; //这也是个实体 public Address address; /** * 提供get和set方法和全参构造函数 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

Test

 public static void main(String[] args) throws Exception { Student student = new Student("小小", 3, "女", null); //将person值赋值给person1 Student student1 = (Student) student.clone(); System.out.println(student); System.out.println(student1); student1.setName("小小她爸"); System.out.println("person 中 name为:" + student.getName()); System.out.println("person1 中 name为:" + student1.getName()); }

1090617-20190313235621488-1930654079.jpg

这里可以已经是两个不同的对象了。但是这里需要注意的是,如果对象中含有对象,这个对象还是浅拷贝。

Address

public class Address { public String city; public int phone; /** * 提供get和set方法和全参构造函数 */ }

Test

 public static void main(String[] args) throws Exception { Address address = new Address("杭州", 1888888888); Student student2 = new Student("小小", 3, "女", address); //将person值赋值给person1 Student student3 = (Student) student2.clone(); address.setCity("北京天安门"); System.out.println("person2 中 city为:" + student2.getAddress().getCity()); System.out.println("person3 中 city为:" + student3.getAddress().getCity()); }

1090617-20190313235642049-1971336257.jpg

我们发现虽然Student是实现了深拷贝,但Address却还是浅拷贝,那如何让Adress也实现深拷贝呢。
Address修改

public class Address implements Cloneable { public String city; public int phone; /** * 提供get和set方法和全参构造函数 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }

Student修改

 //修改clone方法 @Override protected Object clone() throws CloneNotSupportedException { Student s = (Student) super.clone(); s.address = (Address) address.clone(); return s; }

1090617-20190313235631951-1609979398.jpg

弊端: 这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,
那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。
所以还有另一种实现深拷贝方法。

序列化实现深拷贝

//序列化实现深拷贝 public Object deepClone() throws Exception{ // 序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); // 反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } //因为序列化产生的是两个完全独立的对象,所有无论嵌套多少个引用类型,序列化都是能实现深拷贝的。


五、Arrays.copyOf()

之前我误以为Arrays.copyOf()为深拷贝,那只是因为我用的是基本数据类型作为数组,而基本数据类型上面已经说过它没有深浅拷贝这个概念,可以把他理解成只有深拷贝。

 public static void main(String[] args) { //1、基本数据类型 int[] a = {0, 1, 2, 3}; // Arrays.copyOf拷贝 int[] copy = Arrays.copyOf(a, a.length); a[0] = 1; System.out.println(Arrays.toString(copy)); System.out.println(Arrays.toString(a)); //2、对象数组 Student[] stuArr = {new Student("小小", 3, "女"),new Student("小小爸", 29, "男"),new Student("小小妈", 27, "女")}; // Arrays.copyOf拷贝 Student[] copyStuArr = Arrays.copyOf(stuArr, stuArr.length); copyStuArr[0].setName("小小爷爷"); System.out.println(Arrays.toString(stuArr)); System.out.println(Arrays.toString(copyStuArr)); } 

运行结果:

1090617-20190313235732499-1673590724.jpg

可以明显看出,对于基本数据类型只有深拷贝,而对于数组对象而言,明显存在深浅拷贝,而且可以看出Arrays.copyOf()为浅拷贝

原文地址https://www.cnblogs.com/qdhxhz/p/10527245.html

原文链接:https://yq.aliyun.com/articles/693568
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章