类型
拷贝一般分为二大类 引用拷贝 和 对象拷贝,我们通常讲的深拷贝
和浅拷贝
都属于对象拷贝。
引用拷贝
顾名思义,即是对引用地址的拷贝,说明引用地址一样,指向堆中的对象是同一个对象。
如果对一个对象进行改变,其他对象也会跟着改变。
对象拷贝
对象拷贝指 对某一对象进行拷贝,是创建了一个全新的对象,也就是内存中存在二个不同地址的对象,这二个对象的基本数据类型变量的内容值都是一样的,但所包含的对象变量的地址可能一样可能不一样,浅拷贝与深拷贝的区别就在这里
。
现在举个例子:
public static class Person implements Cloneable{
public String name;
public int age;
public House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static class House {
public String address;
public House(String address) {
this.address = address;
}
}
1.浅拷贝
当前对象里包含有对象变量,如果复制的对象中的对象变量和原对象里的对象地址值是相同的,即引用拷贝,则称之为浅拷贝。我们也可以称之为部分拷贝,即拷贝的不彻底。
public static void main(String[] args) {
Person person1=new Person("A",20, new House("aa"));
try {
Person person2= (Person) person1.clone();
System.out.println("persion1 = " + person1+" house = "+person1.house);
System.out.println("persion2 = " + person2+" house = "+person2.house);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
输出结果
persion1 = Person@402a079c house = House@59ec2012
persion2 = Person@4cf777e8 house =House@59ec2012
可以看到我们对Person1进行了clone,person2的内存地址和person1的不一样,说明这是一个新对象,然而在看对象House,内存地址是一样的,说明对于成员变量只进行了内存引用。这个就是浅拷贝。
总结:虽然对象进行了拷贝,但内部变量没有完全进行拷贝。这就是浅拷贝,即拷贝的不彻底
2.深拷贝
当前对象里包含有对象变量,如果复制的对象中的对象变量和原对象里的对象地址值是不同的,即创建了一个新的对象,则称之为深拷贝。我们也可以称之为完全拷贝,内部进行了彻底拷贝。
现在我们对 clone方法做一些修改
public static class Person implements Cloneable {
public String name;
public int age;
public House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
@Override
public Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
//在原有的基础上 clone了成员变量对象
person.house = (House) person.house.clone();
return person;
}
}
public static class House implements Cloneable {
public String address;
public House(String address) {
this.address = address;
}
//实现clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) {
Person person1=new Person("A",20, new House("aa"));
try {
Person person2= (Person) person1.clone();
System.out.println("persion1 = " + person1+" house = "+person1.house);
System.out.println("persion2 = " + person2+" house = "+person2.house);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
现在我们在来clone,看下会有什么结果
persion1 =Person@402a079c house = House@59ec2012
persion2 = Person@4cf777e8 house = House@2f686d1f
可以看到不仅Person对象内存地址不一样,House的内存地址也不一样了,这说明Person1和Person2的内容,成员变量也完全不相同了。
总结:一个对象进行了clone,不仅仅自身clone了一个新的,其内部的成员变量也都clone了一个新的,这就是深拷贝。
所以,如果一个对象中仅包含基本数据类型,则浅拷贝和深拷贝则没区别。如果一个对象中包含了对象变量,则对这些对象变量的拷贝 就成了 浅拷贝和深拷贝的区别。
再通俗一点讲,换皮不换芯,即浅拷贝。换皮又换芯,即深拷贝。
实现深拷贝的方法
1.可以继承Cloneable
接口
@Override
protected Object clone() throws CloneNotSupportedException {
Person newPerson = (Person)super.clone();
//对成员变量进行clone
newPerson.house = (House) newPerson.house.clone();
return newPerson;
}
2.通过对象序列化实现深拷贝
public class Person implements Serializable, Cloneable{
public Object deepClone() {
try {
ByteArrayOutputStream bo= new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bo);
os.writeObject(this);
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return(oi.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}