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深浅拷贝的更多相关文章

  1. Java提高篇——Java实现多重继承

    多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.有些时候我们会认为如果系统中需要使用多重继承往往都是糟糕的设计,这个时候我们往往需 ...

  2. linux(centos8):安装java jdk 15 (java 15)

    一,下载jdk15 官方网站: https://www.oracle.com/java/ 下载页面: https://www.oracle.com/cn/java/technologies/javas ...

  3. Java提高篇——Java 异常处理

    异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用Syst ...

  4. 【python之路15】深浅拷贝及函数

    一.集合数据类型(set):无序不重复的集合,交集.并集等功能 二.三元运算符 三.深浅拷贝 1)字符串和数字:深浅内存地址都一样 2)其他:浅拷贝:仅复制最外面第一层 深拷贝:除了最内层其他均拷贝 ...

  5. 【java提高】---java反射机制

    java反射机制 一.概述 1.什么是反射机制 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态 ...

  6. Java总结——常见Java集合实现细节(1)

    Java提高——常见Java集合实现细节(1) 2018年04月18日 15:07:35 阅读数:25 集合关系图 Set和Map set代表一种集合元素无序.集合元素不可重复的集合 map代表一种由 ...

  7. python基础知识9---字符串拼接,深浅拷贝,三元运算

    一.字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3 ...

  8. java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))

    Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0)   阅读目录 为什么要克隆? 如何实现克隆 浅克 ...

  9. 关于Java的Object.clone()方法与对象的深浅拷贝

    文章同步更新在个人博客:关于Java的Object.clone()方法与对象的深浅拷贝 引言 在某些场景中,我们需要获取到一个对象的拷贝用于某些处理.这时候就可以用到Java中的Object.clon ...

随机推荐

  1. 图片与base64的互转

    /// <summary>        /// 把图片转换到文本信息        /// </summary>        /// <param name=&quo ...

  2. SpringMVC+GSON 对象序列化--日期格式的处理

    Gson异常强大因此使用它代替了Jackson作为SpringMVC消息转换器. 在自己的项目中,发现对象在序列化后,日期格式出现了问题. 先看问题 在员工表中有一列是生日,字段类型为Date,也就是 ...

  3. 架构之微服务(etcd)

    1. ETCD是什么 ETCD是用于共享配置和服务发现的分布式,一致性的KV存储系统.该项目目前最新稳定版本为2.3.0. 具体信息请参考[项目首页]和[Github].ETCD是CoreOS公司发起 ...

  4. STL-Deque 源码剖析

    G++ ,cygnus\cygwin-b20\include\g++\stl_deque.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Comp ...

  5. 重温《STL源码剖析》笔记 第三章

    源码之前,了无秘密. --侯杰 第三章:迭代器概念与traits编程技法 迭代器是一种smart pointer auto_Ptr 是一个用来包装原生指针(native pointer)的对象,声明狼 ...

  6. JavaScript(三、DOM文档对象模型)

    一.什么是DOM DOM 是 Document Object Model(文档对象模型)的缩写. DOM 是 W3C(万维网联盟)的标准. DOM 定义了访问 HTML 和 XML 文档的标准: &q ...

  7. bootstrap-table 列宽问题解决

    <th style="width:120px" data-field="Cel1"><div class="th-inner &qu ...

  8. SVN服务器搭建--Subversio与TortoiseSVN的配置安装(Windows)

    1.  Subversio和TortoiseSVN 简介 Subversio简介: Subversion是一个自由,开源的版本控制系统,可以随意地免费下载.修改.以及重新发布. 是一个通用系统,可以管 ...

  9. jsp页面日期格式不正确

    第一种: 如果是从数据库获取的时间(数据库中日期格式是乱的)可以在数据库取数据时   进行格式化   例如  ;TO_CHAR(SYSDATE,'YYYY-MM-DD') 第二种: 在数据库取出数据后 ...

  10. linux的远程唤醒

    这里主要是针对ubuntu系统的说明 1.检查计算机硬件是否支持WOL(wake on lan)功能. 1.1.检查主板和电源是否支持WOL: 进入BIOS的Power Management Setu ...