1.Object中的clone方法
在Object中的clone方法是这样描述的,如下图(摘自jdk1.8 Object类clone):
如果一个类没有实现Cloneable接口,如果调用对象.clone则会抛出CloneNotSupportedException异常。
public class Clone {
private int a;
String b = "123";
public static void main(String[] args) throws CloneNotSupportedException {
Clone clone = new Clone();
Clone clone1 = (Clone) clone.clone();
System.out.println();
}
}
下面我们对Clone类实现Cloneable接口
public class Clone implements Cloneable{
private int a;
private String b = "123";
private Student c = new Student(1,"str");
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Clone clone = new Clone();
Clone clone1 = (Clone) clone.clone();
System.out.println();
}
}
执行结果如下:
由上图可以看出,clone对象和clone1对象的内存地址是不一样的,表明确实重新开辟一个空间,而不是两个不同的引用指向同一个内存空间,但是可以看到两个对象中的成员变量c指向的是同一个对象,这表明
1.对于基本数据类型,是直接拷贝的(也可以理解为深拷贝)
2.对于引用数据类型,没有进行拷贝,是两个不同的引用指向了同一个对象
所以说我们修改clone对象的成员变量a不影响clone1对象的成员变量a
public class Clone implements Cloneable{
private int a;
private String b = "123";
private Student c = new Student(1,"str");
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Clone clone = new Clone();
Clone clone1 = (Clone) clone.clone();
clone.a = 1;
System.out.println();
}
}
如果我们修改clone对象的成员变量c对象的中的a成员变量,则clone对象和clone1对象中都要改变
public class Clone implements Cloneable{
private int a;
private String b = "123";
private Student c = new Student(1,"str");
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Clone clone = new Clone();
Clone clone1 = (Clone) clone.clone();
clone.c.setA(123333);
System.out.println();
}
}
2.数组的拷贝
2.1 一维数组
对于基本数据类型的数组,使用clone方法是深拷贝
int[] array1 = new int[]{1,2,3};
int[] array2 = array1.clone();
array2[1] = 3;
在array2完成对array1的拷贝后,我们对array2中数组下标为1赋值为3,不影响array1中的值,执行结果如下:
对于引用数据类型的数组,使用clone方法是浅拷贝
Clone[] clones1 = new Clone[]{new Clone(),new Clone()};
Clone[] clones2 = clones1.clone();
执行结果如下:
可以看到两个引用是不同的对象,但是clones1指向对象的数组下标0和clones2指向对象的数组下标0是指向了同一个对象。
2.2 二维数组
对于基本数据类型的二维数组如int[][] arrays,因为java中没有二维数组的概念,只有数组的数组,所以二维数组arrays中存储的实际上是一维数组的引用。当调用clone函数时,是对这两个引用进行了复制。
int[][] arrayd1 = new int[][]{{1,2,3},{4,5,6}};
int[][] arrayd2 = arrayd1.clone();
执行结果如下:
arrayd1[0]和arrayd2[0]是同一个对象,所以说对于二维数组不能直接用clone进行深拷贝。
2.3 Arrays.copy()
Arrays.copy()可以实现对数组的拷贝,跟踪其源码,可以看到:
最终调用的是System.arraycopy(),下面我们来使用Arrays.copy()进行拷贝。
基本数据类型:
int[] array1 = new int[]{1,2,3};
int[] array2 = Arrays.copyOf(array1, array1.length);
array2[1] = 3;
执行结果:
可以看到和使用clone方法一致
引用数据类型:
Clone[] clones1 = new Clone[]{new Clone(),new Clone()};
Clone[] clones2 = Arrays.copyOf(clones1, clones1.length);
执行结果:
和使用clone方法一致。
总结
(1)一个对象要想使用Object类方法中clone(),那么必须要实现Cloneable接口,否则会抛出CloneNotSupportedException异常
(2)对象的clone,对于对象中基本数据类型成员变量是完全复制一份,对于引用类型成员变量是两个不同的指针指向同一个对象
(3)引用数据类型一维数组使用clone时不需要实现Cloneable接口
(4)基本数据类型一维数组的clone是完全的复制了一份,引用数据类型一维数组的clone生成了新的对象,但是两个对象同一个数组下标指向的对象是同一个
(5)基本数据类型二维数组的clone和引用数据类型一维数组的clone一致
有待思考
java中深拷贝如何实现?