clone方法就是返回一个原对象的拷贝,默认走的是浅拷贝。克隆的目的是复制对象,但是新的对象是独立于原来的对象的,一般我们克隆出来的对象都在一些属性做了更改,这个时候需要小心一点,如果更改的属性是引用数据类型,可能会影响到原来的对象,如果都是基本数据类型则不怕。使用clone方法的前提是继承Cloneable接口,数组默认实现了Cloneable接口,默认走的是浅拷贝。深克隆与浅克隆的区别:浅克隆后的对象中的引用类型属性的修改会影响原对象中的内容。深克隆不会影响
克隆方法的调用方法:实现Cloneable接口,重写Object的clone()方法,并把protected修饰符改为public
那么问题来了,什么是浅拷贝?什么是深拷贝呢?
1.浅克隆(shadow clone) 只用clone方法
克隆就是复制一个对象的复本.若只需要复制对象的字段值(对于基本数据类型,如:int,long,float等,则复制值;对于引用数据类型仅复制该字段值,如数组变量则复制地址,对于对象变量则复制对象的reference,因此是引用传递,复制的对象若改变其中复合数据,会影响被复制对象中的复合数据,因为他们指向的是同一个地址。
2.深克隆(deep clone)
可以通过三种方式实现,前两种适用于引用类型变量较少的时候。
方法一:简单clone对象后,对象中的引用类型变量再次进行clone。
方法二:简单clone对象后,创建新的引用类型变量进行赋值。
方法三:序列化对象,所有类都需要实现Serializable接口。类中的所有引用类型变量对应的类都需要实现序列化。
关于序列化,任何类型只要实现了Serializable接口,就可以被保存到文件中,或者作为数据流通过网络发送到别的地方。也可以用管道来传输到系统的其他程序中。可以参考文章——>
实现序列化接口时,private static final long serialVersionUID 的作用
深克隆与浅克隆的区别在于对复合数据类型的复制。若对象中的某个字段为复合类型,在克隆对象的时候,需要为该字段重新创建一个对象,两个对象指向两个不同的引用。
自己写的例子如下:
import java.util.Arrays;
import java.util.Date;
public class FileTest extends DeepClone implements Comparable<FileTest>,Cloneable{
private static final long serialVersionUID = 1L;
private int a = 1;
private int b = 2;
private int[] c = new int[]{4,5,6};
private Date date = new Date() ;
public int getSize(){
return this.getA()*this.getA()*2;
}
public FileTest(int a,int b,int[] c){
this.a = a;
this.b = b;
this.c = c;
}
@Override
public Object clone() {//重写Object的clone方法
try {
//只执行这一步,是浅克隆(shadow clone)
FileTest f = (FileTest)super.clone();
//若想深克隆(deep clone),需要将复合成员变量进行值复制,而不是引用复制
//深克隆方法一(直接再将引用对象clone)
int[] d1 = (int[])c.clone();
f.setC(d1);
//深克隆方法二(相对来说比较笨拙)
int[] c = f.getC();
int[] d2 = new int[c.length];
for(int i=0;i<c.length;i++) {
d2[i] = c[i];
}
f.setC(d2);
//深克隆方法一(对复合对象date进行深克隆)
Date date = f.getD();
f.date = (Date) this.getD().clone();
return f;
}catch (CloneNotSupportedException e){
return null;
}
}
public static void main(String[] args) throws Exception {
FileTest f1 = new FileTest(5,7,new int[]{1,2,3});
FileTest f2 = new FileTest(1,2,new int[]{4,5,6});
FileTest f3 = new FileTest(4,3,new int[]{7,8,9});
//使用compareTo方法
System.out.println(f1.compareTo(f2));
//因为实现了compareTo方法,因此可以直接调用Arrays的sort方法
FileTest[] array = new FileTest[3];
array[0] = f1;
array[1] = f2;
array[2] = f3;
Arrays.sort(array);
for(FileTest f:array){
System.out.println(f.getSize());
}
//FileTest实现了Cloneable接口并重写了Object的clone方法,重定义了clone方法
FileTest f4 = (FileTest) f1.clone();
// System.out.println(f1.getC().toString());
// System.out.println(f4.getC().toString());
System.out.println(f1.getD()==f4.getD());
//重写了equals方法
System.out.println(f1.equals(f4));
//深克隆方法三(序列化)
FileTest f5 = (FileTest) f1.deepClone();
System.out.println(f1.getD()==f5.getD());
System.out.println(f1 == f5);
}
@Override
public int compareTo(FileTest o) {//注意:这里传参需要传自定义类,因为Compareable是泛型接口,compareTo方法形参也是泛型
if(this.getSize() > o.getSize()){ //根据getSize()方法比较两个对象的大小
return 1;
}else if(this.getSize() == o.getSize()){
return 0;
}else{
return -1;
}
}
@Override
public boolean equals(Object o){ //如果想要比较两个对象,最好也重写Object的equals方法,且实现逻辑与compareTo保持一致
FileTest f = (FileTest)o;
if(f.getSize() == this.getSize()){
return true;
}
return false;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
public int[] getC() {
return c;
}
public void setA(int a) {
this.a = a;
}
public void setB(int b) {
this.b = b;
}
public void setC(int[] c) {
this.c = c;
}
public Date getD() {
return date;
}
}
import java.io.*;
public class DeepClone implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 利用序列化和反序列化进行对象的深拷贝
* @return
* @throws Exception
*/
protected 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();
}
}
一些深度思考(来自《Java语言程序设计与数据结构》P452):