java提高(15)---java深浅拷贝
java提高(15)---java深浅拷贝
一、前言
为什么会有深浅拷贝这个概念?
我觉得主要跟JVM内存分配有关,对于基本数据类型,只存在栈内存,所以它的拷贝不存在深浅拷贝这个概念。而对于对象而言,一个对象的创建会在内存中分配两块空间,一个在栈内存存对象的引用指针,一个在堆内存存放对象。这个时候会有一个问题,你拷贝的只是这个引用指针还是拷贝两块内存一起拷贝,这个时候就会有深浅拷贝一说。
还有之前我认为Arrays.copyOf()是深度拷贝,亲测后发现原来它也是浅拷贝。下面进行具体说明。
二、数据类型
数据分为基本数据
类型(int, boolean, double, byte, char等)和对象数据
类型。
基本数据类型的特点:直接存储在栈(stack)中的数据
.
引用数据类型的特点:在栈内存存储对象引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
三、什么是浅拷贝和深拷贝
首先需要明白,深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的
。那先来看看浅拷贝和深拷贝的概念。
在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 =
号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。
浅拷贝
:如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象。
深拷贝
:在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量。
深拷贝和浅拷贝的示意图大致如下:
具体接下来代码演示。
四、代码演示
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()); }
查看运行结果
从图片中我们可以很明显看出,它们指向的内存地址是一致的,同样我改变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()); }
这里可以已经是两个不同的对象了。但是这里需要注意的是,如果对象中含有对象,这个对象还是浅拷贝。
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()); }
我们发现虽然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; }
弊端
: 这里我们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)); }
运行结果:
可以明显看出,对于基本数据类型只有深拷贝,而对于数组对象而言,明显存在深浅拷贝,而且可以看出Arrays.copyOf()为浅拷贝
。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2019五个最棒的机器学习课程
凭借强大的统计学基础,机器学习正在成为最有趣,节奏最快的计算机科学领域之一,目前已经有无穷无尽的行业和应用正在使用机器学习使它们更高效和智能。 聊天机器人、垃圾邮件过滤、广告投放、搜索引擎和欺诈检测是机器学习模型正在实际应用于日常生活的几个例子。 机器学习到底是什么呢?我认为机器学习是让我们找到模式并为人类无法做的事情创建数学模型。 机器学习课程与包含探索性数据分析,统计,通信和可视化技术等主题的数据科学课程不同,它更侧重于教授机器学习算法,如何以数学方式工作,以及如何在编程语言中使用它们。 以下是今年五大机器学习课程的简要回顾。 最好的五个机器学习课程: 1.机器学习-Coursera 2.深度学习专项课程-Coursera 3.使用Python进行机器学习-Coursera 4.高级机器学习专项课程-Coursera 5.机器学习-Ed
- 下一篇
WEB 实时推送技术的总结
前言 随着 Web 的发展,用户对于 Web 的实时推送要求也越来越高 ,比如,工业运行监控、Web 在线通讯、即时报价系统、在线游戏等,都需要将后台发生的变化主动地、实时地传送到浏览器端,而不需要用户手动地刷新页面。本文对过去和现在流行的 Web 实时推送技术进行了比较与总结。 本文完整的源代码请猛戳Github博客,纸上得来终觉浅,建议大家动手敲敲代码。 一、双向通信 HTTP 协议有一个缺陷:通信只能由客户端发起。举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。在WebSocket协议之前,有三种实现双向通信的方式:轮询(polling)、长轮询(long-polling)和iframe流(streaming)。 1.轮询(polling) 轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。其缺点也很明显:连接数会很多,一个接受,一个发送。而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CP...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8编译安装MySQL8.0.19