当需要创建一个对象拷贝时,就需要使用clone方法。为了使一个类的对象可以拷贝,那么这个类必须要实现Cloneable()接口。
Java 库中的很多类( 例如, Date、 Calendar 和 ArrayList) 实现 Cloneable。 这样, 这些类的实例可以被克隆。
clone方法实现的浅复制,当复制一个对象到另一个相同类型的对象时,如果这个对象中的某些数据类型是属于基本数据类型,此时复制的就是它的值。但当某些数据是一个对象时,此时复制的是这个对象的引用。
当对一个数组列表进行克隆时,实际上执行的也是浅复制,但可能会有一定的迷惑性。先看以下代码:
ArrayList<String> list = new ArrayList<>() ;
list.add("New York");
ArrayList<String> list1 = list;
当执行上面的赋值操作时,实际上是将list的引用给了list1,所以此时二者所指向的内存块是相同的。
再看下面的代码:
ArrayList<String> list2 = (ArrayList<String>) (list.clone()) ;
list.add("heze");
当我们使用了clone方法后,我们可以先设想一下list2现在是个什么状态。上面我们说过clone是浅复制,所以这里我们可以会觉得现在list2和list是指向同一内存块的。那么我再往list中添加对象时,此时list2也会相应的改变,那么实际上是不是这样的呢?我们来运行并将二者打印一下:
[New York, heze]
[New York]
从结果可以看出list2中并没有被添加对象“heze”。
我们再对list进行如下操作:
list.set(0, "shanxian"); // (2)
我们将list的第零个对象设置为“shanxian”,运行并打印一下:
[shanxian, heze]
[New York] // (3)
我们发现list2还是没有变化?这是为什么呢?
先明确一点,数组列表中存储的是对每个对象的引用,当使用clone()方法时,并不是将整个数组列表的地址进行复制,而是将被克隆数组列表中存储的每个对象的引用进行了复制。list2数组是一个新的数组,对list的任何操作都不会对list2产生影响。这一点可以从代码(2)处看出。当我们重新设置list的第0个对象时,只不过是将对象“shanxian”的引用赋值给了list的第0号元素。此时list(0)将不再指向对象“NewYork”。但此时因为list2(0)还保持着对“NewYork”的引用,所以对象“NewYork”并不会被虚拟机清理。这也就解释了代码(3)处的现象。
总结:
数组列表中存储的是对每个对象的引用,当使用clone方法时,复制的是每个对象的引用。