java提高(15)---java深浅拷贝
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()为浅拷贝。
只要自己变优秀了,其他的事情才会跟着好起来(少将2)
java提高(15)---java深浅拷贝的更多相关文章
- Java提高篇——Java实现多重继承
多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...
- linux(centos8):安装java jdk 15 (java 15)
一,下载jdk15 官方网站: https://www.oracle.com/java/ 下载页面: https://www.oracle.com/cn/java/technologies/javas ...
- Java提高篇——Java 异常处理
异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用Syst ...
- 【python之路15】深浅拷贝及函数
一.集合数据类型(set):无序不重复的集合,交集.并集等功能 二.三元运算符 三.深浅拷贝 1)字符串和数字:深浅内存地址都一样 2)其他:浅拷贝:仅复制最外面第一层 深拷贝:除了最内层其他均拷贝 ...
- 【java提高】---java反射机制
java反射机制 一.概述 1.什么是反射机制 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态 ...
- Java总结——常见Java集合实现细节(1)
Java提高——常见Java集合实现细节(1) 2018年04月18日 15:07:35 阅读数:25 集合关系图 Set和Map set代表一种集合元素无序.集合元素不可重复的集合 map代表一种由 ...
- python基础知识9---字符串拼接,深浅拷贝,三元运算
一.字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3 ...
- java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))
Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0) 阅读目录 为什么要克隆? 如何实现克隆 浅克 ...
- 关于Java的Object.clone()方法与对象的深浅拷贝
文章同步更新在个人博客:关于Java的Object.clone()方法与对象的深浅拷贝 引言 在某些场景中,我们需要获取到一个对象的拷贝用于某些处理.这时候就可以用到Java中的Object.clon ...
随机推荐
- CF877F
题目大意:给定n个数有正有负,有Q次询问,每次询问区间[l,r]中有几个子区间满足和为k 做法:显然的莫队 每次用map记录一下当前区间[l,r]中的前缀和的值的个数 然后r的话找sum[r]-k的, ...
- Lintcode399 Nuts & Bolts Problem solution 题解
[题目描述] Given a set of n nuts of different sizes and n bolts of different sizes. There is a one-one m ...
- 第一课:Hadoop集群环境搭建
一. 检查列表 1.1.网络访问 设置电脑IP以及可以访问网络设置:进入etc/sysconfig/network-scripts/,使用命令"ls -all" 查看文件.会看到i ...
- python3+requests:使用类封装接口测试脚本
前言:接口测试用例较多,我们不可能每个用例都写一次requests,get或者requests,post等,所以对共用方法要进行封装处理 第一次修改:将get请求和post请求单独定义出来,使用过程中 ...
- js基础进阶--编的实用技巧(一)
我的个人博客:http://www.xiaolongwu.cn 在平时的开发中,编码技巧很重要,会让你少写很多代码,起到事倍功半的效果. 下面总结几种简单的技巧,大家共同学习一下 1. 利用+.-./ ...
- .net如何使用系统中没有安装的字体?
不想安装到客户端的 Fonts 目录下面,但是我又想在程序中使用它. 这段代码放在哪里? 字体文件需要放到要安装的机器上吗?并不需要 System.Drawing.Text.PrivateFontCo ...
- PiggyMetrics windows 部署
PiggyMetrics 是springcloud的demo,其特性就不细说了,主要描述在win10下部署的坑. 官网是:https://github.com/sqshq/PiggyMetrics 官 ...
- 小苹果WP(实验吧-隐写术)
本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=706 前言:本文是实验吧训练题库里隐写术部分的一道题:"小苹果"的Write Up. 题目链接: ...
- Day1 《机器学习》第一章学习笔记
<机器学习>这本书算是很好的一本了解机器学习知识的一本入门书籍吧,是南京大学周志华老师所著的鸿篇大作,很早就听闻周老师大名了,算是国内机器学习领域少数的大牛了吧,刚好研究生做这个方向相关的 ...
- 集成学习之Boosting —— AdaBoost原理
集成学习大致可分为两大类:Bagging和Boosting.Bagging一般使用强学习器,其个体学习器之间不存在强依赖关系,容易并行.Boosting则使用弱分类器,其个体学习器之间存在强依赖关系, ...