说在前面

在进入理解clone前,我们需要对“基本数据类型”和“引用数据类型”的存储模式有一个清晰的认识。

基本数据类型,变量的内容保存的是实际的值;引用数据类型,变量的内容保存的是一个地址,该地址的指向才是实际的值。

int baseData = 5; // 基本数据类型,baseData对应的内存保存的是具体的值:5

System.out.println(baseData); // 直接打印,返回:5

HandClass handData = new HandClass(); //引用数据类型,handData对应的内存保存的是一个16进制的内存地址,该地址才是保存值的地方

System.out.println(handData); //直接打印,返回内存地址:HandClass@3c6f579

需要注意的是,不管是基本数据类型,还是引用数据类型,赋值(=)操作都是将变量自身内容赋值给另一个变量。唯一不同的是,我们常用操作中,基本数据类型是针对自身内容(值)进行的;而引用数据类型则是针对自身内容(地址)的指向进行的。

int data1 = 1;

int data2 = data1; // 将data1的内容(1)赋值给data2

System.out.println(data1); // 返回:1

System.out.println(data2); // 返回:1

data1 = 2; // 即使修改data1的内容,data2不受影响

System.out.println(data1); // 返回:2

System.out.println(data2); // 返回:1

System.out.println("--------------------");

HandClass handData1 = new HandClass();

handData1.ele = 1;

HandClass handData2 = handData1; // 将handData1的内容(实际内存地址)赋值给handData2

System.out.println(handData1.ele); // 返回:1

System.out.println(handData2.ele); // 返回:1

// 直接将handData1和handData2打印出来,返回相同的内容(地址)

System.out.println(handData1); // 返回:HandClass@66edc3a2

System.out.println(handData2); // 返回:HandClass@66edc3a2

handData1.ele = 2; // 修改handData1.ele的内容,handData2受影响,因为他们的内容相同(指向了同一个内存)

System.out.println(handData1.ele); // 返回:2

System.out.println(handData2.ele); // 返回:2

handData1 = new HandClass(); // 为handData1开辟一个新的内地址,并将该地址赋值给handData1的内容

// 直接将handData1和handData2打印出来,返回不相同的内容(地址):handData1的内容

System.out.println(handData1); // 返回:HandClass@3ced0338

System.out.println(handData2); // 返回:HandClass@66edc3a2

handData1.ele = 3; // 此时再次修改handData1.ele的内容,handData2不受影响,因为他们的内容已经不相同(指向了不同的内存)

System.out.println(handData1.ele); // 返回:3

System.out.println(handData2.ele); // 返回:2

总结:无论是基本数据类型,还是引用数据类型,赋值操作的实质都是内容赋值。

进入正题

先抛出结论:数组的clone方法,本质是“降维赋值”。即将数组进行降低维度后,开辟一个与之一摸一样的内存空间,然后进行遍历赋值。

(此文不讨论数组的存储模式,对数组存储模式比较薄弱的朋友,请先自行了解)

一维数组:

一维数组降维后是一组变量。

int intArrayA[] = new int[]{1,2,3};

int intArrayB[] = intArrayA.clone(); // 将intArrayA的克隆赋值给intArrayB

/**

* 先对intArrayA进行降维

* 降维后,变成一组变量:intArrayA[0]、intArrayA[1]、intArrayA[2]

* 在内存中申请一组与intArrayA类型、长度相同数组: int tmp[] = new int[2];

* 将变量进行遍历赋值:tmp[0]=intArrayA[0]、tmp[1]=intArrayA[1]、tmp[2]=intArrayA[2]

* 将数组tmp的内容(地址)返回(注:tmp是一个数组,即属于引用类型)

* */

System.out.println(intArrayA[1]); // 返回:2

System.out.println(intArrayB[1]); // 返回:2

intArrayA[1] = 100;

System.out.println(intArrayA[1]); // 返回:100

System.out.println(intArrayB[1]); // 返回:2

/**

* 上述结论:"无论是基本数据类型,还是引用数据类型,赋值操作的实质都是内容赋值。"

* intArrayA降维后,intArrayA[0]~intArrayA[2]是一组基本数据类型的变量

* 赋值的时候,将intArrayA[0]~intArrayA[2]的内容(实际的值)赋值给tmp[0]~tmp[2]

* 而后tmp[0]~tmp[2]组成的tmp的内容(一个地址)又返回给intArrayB

* 因此,intArrayB[1]和intArrayA[1]的内容是一致的,他们的内容均是"2"

* 当我们通过intArrayA[1]进行操作时,仅仅是修改自身的内容,intArrayB[1]不会受到影响

* */

System.out.println("--------------------");

HandClass handArrayA[] = new HandClass[]{new HandClass(),new HandClass(),new HandClass()};

HandClass handArrayB[] = handArrayA.clone();

/**

* 先对handArrayA进行降维

* 降维后,编程一组变量:handArrayA[0]、handArrayA[1]、handArrayA[2]

* 在内存中申请一组与handArrayA类型长度、相同数组: HandClass tmp[] = new HandClass[2];

* 将变量进行遍历赋值:tmp[0]=handArrayA[0]、tmp[1]=handArrayA[1]、tmp[2]=handArrayA[2]

* 将数组tmp的内容(地址)返回(注:tmp是一个数组,即属于引用类型)

* */

System.out.println(handArrayA[1].ele); // 返回:0 注:此处的0,是实例化时,系统赋与的默认初始值

System.out.println(handArrayB[1].ele); // 返回:0

handArrayA[1].ele = 100;

System.out.println(handArrayA[1]); // 返回:HandClass@7b1ddcde

System.out.println(handArrayB[1]); // 返回:HandClass@7b1ddcde

System.out.println(handArrayA[1].ele); // 返回:100

System.out.println(handArrayB[1].ele); // 返回:100

/**

* 上述结论:"无论是基本数据类型,还是引用数据类型,赋值操作的实质都是内容赋值。"

* handArrayA降维后,handArrayA[0]~handArrayA[2]是一组引用类型的变量

* 赋值的时候,将handArrayA[0]~handArrayA[2]的内容(一个地址)赋值给tmp[0]~tmp[2]

* 而后tmp[0]~tmp[2]组成的tmp的内容(一个地址)又返回给handArrayB

* 因此,handArrayB[1]和handArrayA[1]的内容是一致的,他们均指向了同一个内存

* 当我们通过handArrayA[1]进行操作时(实际是修改其内容对应的实际对象的内容),handArrayB[1](内容所指向的实际对象)也会受到影响

* */

二维及多维数组:

二维数组降维后是一组数组,数组本身就是引用类型。因此二维及多维的数组的克隆中的赋值,均属于引用类型赋值。

int multIntArrayA[][] = new int[][]{{11,12,13},{21,22,23}};

int multIntArrayB[][] = multIntArrayA.clone();

/**

* 先对multIntArrayA进行降维

* 降维后,变成一组一维数组:multIntArrayA[0]、multIntArrayA[1]

* 在内存中申请一组与multIntArray类型、长度相同数组: int tmp[][] = new int[2][3];

* 将数组进行遍历赋值:tmp[0]=multIntArray[0]、tmp[1]=multIntArray[1],

* 特别着重说明的事,这里是数组(引用类型)的赋值,而并非数组元素(int类型)的赋值

* 将数组tmp的内容(地址)返回(注:tmp是一个数组,即属于引用类型)

* */

System.out.println(multIntArrayA[0][1]); // 返回:12

System.out.println(multIntArrayB[0][1]); // 返回:12

multIntArrayA[0][1] = 66;

System.out.println(multIntArrayA[0][1]); // 返回:66

System.out.println(multIntArrayB[0][1]); // 返回:66

/**

* 我们注意到,multIntArrayB已经受到了multIntArrayA的影响

* 因为clone只会降低一个维度后进行遍历赋值,即:将multIntArrayA[0]的内容(一个地址)赋值给multIntArrayB[0]

* 当我们操作multIntArrayA[0][1]时,实际是操作了multIntArrayA[0]的内容所指向的实际的数组第一个元素的值

* multIntArrayB[0]保存的内容与multIntArrayA[0]一致,同样指向的是同一个数组

* 因此multIntArrayB[0][1]全等于multIntArrayA[0][1](变量自身一模一样)

* 再次也可以明确,clone的降维,只会降低一个维度

* 若要数组元素也克隆,则需要再次clone,以将维度降低至数组元素

* */

multIntArrayB[0] = multIntArrayA[0].clone();

multIntArrayA[0][1] = 77;

System.out.println(multIntArrayA[0][1]); // 返回:77

System.out.println(multIntArrayB[0][1]); // 返回:66

/**

* 可以看出,当我们对multIntArrayA[0]进行克隆

* multIntArrayA[0]降维后,就是一组基本数据类型的变量(int)

* 因此multIntArrayB[0]中的各个元素(基本数据类型)全等于multIntArrayA[0]中的元素,而是一个"克隆品"

* 当我们修改multIntArrayA[0][1]后,multIntArrayB[0][1]并不会随之变化

* 为了加强验证这一现象,我们将"基本数据类型"改为"引用数据类型"

* 如果我们的猜想没错,进行上述操作后,最后输出的应该也一样是"77",而不是"66"

* */

System.out.println("--------------------");

HandClass multHandArrayA[][] = new HandClass[][]{{new HandClass(),new HandClass(),new HandClass()},{new HandClass(),new HandClass(),new HandClass()}};

HandClass multHandArrayB[][] = multHandArrayA.clone();

System.out.println(multHandArrayA[0][1]); // 返回:HandClass@7b1ddcde

System.out.println(multHandArrayB[0][1]); // 返回:HandClass@7b1ddcde

multHandArrayA[0][1].ele = 66;

System.out.println(multHandArrayA[0][1].ele); // 返回:66

System.out.println(multHandArrayB[0][1].ele); // 返回:66

multHandArrayB[0] = multHandArrayA[0].clone();

multHandArrayA[0][1].ele = 77;

System.out.println(multHandArrayA[0][1].ele); // 返回:77

System.out.println(multHandArrayB[0][1].ele); // 返回:77

/**

* 如果只是进行multHandArrayA的clone,那么"基本数据类型"和"引用数据类型"是一样的

* 但是,当我们再次对multHandArrayA[0]进行克隆后,效果就不一样了

* 由于multHandArrayA[0]降维后,是一组引用数据类型的数组

* 因此multHandArrayB[0]中的各个元素(引用数据类型)的内容与multIntArrayA[0]中对应的元素一致,即指向同一个地址

* 当我们修改multHandArrayA[0][1]的值(实际修改的是它内容指向的地址对应的实际对象),multHandArrayB[0][1]也会随之变化

* */

总结:Java中,数组的克隆(clone)只会降一次维,而后开辟一块新的空间,遍历所有元素进行赋值操作。

值得一提

一维数组,由于降维后就是数组的基本元素,因此看起来就像是进行了“深拷贝”,实际是错误的。只有基本数据类型的一维数组的clone才是“深拷贝”的效果;引用数据类型的一维数组clone,还需要额外进行“对象拷贝”;

二维或者多维数组可以通过递归方式,进行更低维度(直至降低至一维)的clone,从而达到“深拷贝”的目的。

java数组的clone方法_深入浅出,如何更彻底地理解Java数组的clone方法相关推荐

  1. java 写文件 权限不够_教你解决Linux系统中JAVA创建文件后权限不足的问题

    在作业中,项目使用文件上传. 这个功能很常见. 当Kai Ge今天更改其官方帐户时,他遇到了一个问题,即无法访问下载的文件,也无法通过浏览器访问该文件. 它是怎么发生的? 经过许多问题之后,事实证明这 ...

  2. java 反射 父类的属性_用反射的方式获取父类中的所有属性和方法

    package com.syh.jdbc.reflection_super; /** * 父类 * @author syh * */ public class Parent { public Stri ...

  3. java list有序还是无序_牛批!2w字的Java集合框架面试题精华集(2020最新版),赶紧收藏。...

    一个多月前,作者和一些小伙伴决定做一系列的 Java 知识点常见重要问题的小册,方便用来夯实基础!小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标 ...

  4. java set第n位_数据结构与算法——常用数据结构及其Java实现

    本文采用Java语言来进行描述,帮大家好好梳理一下数据结构与算法,在工作和面试中用的上.亦即总结常见的的数据结构,以及在Java中相应的实现方法,务求理论与实践一步总结到位. 常用数据结构 数组 数组 ...

  5. java的简单算法题_[2]十道算法题【Java实现】

    前言 清明不小心就拖了两天没更了-- 这是十道算法题的第二篇了-上一篇回顾:十道简单算法题 最近在回顾以前使用C写过的数据结构和算法的东西,发现自己的算法和数据结构是真的薄弱,现在用Java改写一下, ...

  6. java 时间转为毫秒数_疫情期间面试总结一(java基础方面)

    一.线程池关键参数以及运行原理 1.corePoolSize(核心线程数):线程池中线程的核心数量,线程池创建后池中并没有线程,而是等有任务的时候才去创建线程,如果任务过多并且核心线程数已经用完,则新 ...

  7. java byte char io流_一文带你看懂JAVA IO流,史上最全面的IO教学

    原标题:一文带你看懂JAVA IO流,史上最全面的IO教学 一.IO流是什么 惯例引用百科的回答 流是一种抽象概念,它代表了数据的无结构化传递.按照流的方式进行输入输出,数据被当成无结构的字节序或字符 ...

  8. java io流详解_一文带你看懂JAVA IO流,史上最全面的IO教学啦

    一.IO流是什么 惯例引用百科的回答流是一种抽象概念,它代表了数据的无结构化传递.按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列.从流中取得数据的操作称为提取操作,而向流中添加数据的操作 ...

  9. java 笔试题一套_软世通分享一套Java笔试题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 7.以下程序段执行后的K值为( ). int x=20; y=30; k=(x>y)?y:x A. 20 B. 30 C.10 D.50 8.要想定 ...

最新文章

  1. ESPNet系列:自动驾驶领域轻量级分割模型
  2. Ubuntu安装MySQL1 - 失败
  3. 中国计算机学会CCF推荐国际学术会议和期刊目录-软件工程/系统软件/程序设计语言
  4. POj 3420 Quad Tiling 状态压缩DP+递推+矩阵快速幂
  5. 艾伟:ASP.NET 与 AJAX的实现方式
  6. 2021快手奢侈品行业数据价值报告
  7. 9 | Spatial-based GNN/convolution模型之GIN
  8. SpringMVC学习系列-解决GET请求时中文乱码的问题
  9. YAF 菜鸟的学习笔记
  10. opencv实现阈值分割
  11. 车机没有carlife可以自己下载吗_论互联哪家强 Carlife/Carplay针尖对麦芒
  12. Spring实战(使用数据)
  13. 数字版权管理 (DRM) 续
  14. 介绍一个使用go写的TUI性能监测工具gotop
  15. VUzzer: Application-aware Evolutionary Fuzzing
  16. [hiho 14]并查集
  17. photoshop批处理改变图片大小
  18. 解密微信开放高级接口 企业如何应对
  19. Python股票分析系列——数据整理和绘制.p2
  20. 0ra-12170 tns 连接超时

热门文章

  1. python 2021/12/31
  2. 如何将ISE的库和vivado的库都加入modelsim.ini
  3. 乐高积木搭建微型地球仪,lego微型地球仪搭建详细流程
  4. 【体验】感谢朋友雪中送炭寄来的便携示波器,便携示波器开箱体验
  5. 利用SIMULINK搭建一个16QAM调制解调收发系统
  6. 7.MATLAB变量——矩阵操作二
  7. 十二、linux LED初始化
  8. 无RTOS下使用队列出现的问题记录
  9. 性能计数器取网卡流量
  10. Mysql SQL Mode详解