说在前面
在进入理解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,从而达到“深拷贝”的目的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。