深浅克隆
浅克隆:被复制对象的变量都含有与原来对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深克隆:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量指向复制的新对象,而不再是原有对象的引用。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
Java的clone()方法【定义在Object类中】
clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
- 对任何的对象x,都有x.clone() !=x,克隆对象与原对象不是同一个对象;
- 对任何的对象x,都有x.clone().getClass()= =x.getClass(),克隆对象与原对象的类型一样;
- 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。equals()方法定义恰当,即是说你重写了这个方法;
Java中对象的克隆
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法,在派生类中覆盖基类的clone()方法,并声明为public【Object类中的clone()方法为protected的】,在派生类的clone()方法中,调用super.clone(),在派生类中实现Cloneable接口;
重要说明
为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
继承自java.lang.Object类的clone()方法是浅复制;
浅克隆实例
<span style="font-size:18px;">package clone;
public class Student implements Cloneable{
private String name;
private int age;
private Teacher teacher;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
//重写父类clone方法
protected Student clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return (Student) super.clone();
}
}
</span>
<span style="font-size:18px;">package clone;
public class Teacher implements Cloneable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected Teacher clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return (Teacher) super.clone();
}
}
</span>
<span style="font-size:18px;">package clone;
public class TestSimpleClone {
public static void main(String[] args) throws CloneNotSupportedException{
Student stu1 = new Student();
stu1.setName("学生1");
stu1.setAge(21);
Teacher teacher = new Teacher();
teacher.setName("老师1");
teacher.setAge(29);
stu1.setTeacher(teacher);
System.out.println("克隆前,原对象: 学生的名字:"+stu1.getName()+";学生的年龄: "+stu1.getAge()+
";老师的名字:"+stu1.getTeacher().getName()+";老师的年龄"+stu1.getTeacher().getAge());
Student clone = stu1.clone();
System.out.println("克隆学生的名字:"+clone.getName()+";克隆学生的年龄: "+clone.getAge()+
";克隆老师的名字:"+clone.getTeacher().getName()+";克隆老师的年龄"+clone.getTeacher().getAge());
//修改克隆对象,看是否修改了原对象
clone.setName("学生2");
clone.setTeacher(clone.getTeacher());
//修改克隆老师的名字,查看原学生的老师的名字是否改变
clone.getTeacher().setName("老师2");
System.out.println("克隆后修改克隆对象,原对象: 学生的名字:"+stu1.getName()+";学生的年龄: "+stu1.getAge()+
";老师的名字:"+stu1.getTeacher().getName()+";老师的年龄"+stu1.getTeacher().getAge());
}
}
</span>
运行结果:
<span style="font-size:18px;">克隆前,原对象: 学生的名字:学生1;学生的年龄: 21;老师的名字:老师1;老师的年龄29
克隆学生的名字:学生1;克隆学生的年龄: 21;克隆老师的名字:老师1;克隆老师的年龄29
克隆后修改克隆对象,原对象: 学生的名字:学生1;学生的年龄: 21;<span style="background-color: rgb(255, 0, 0);">老师的名字:老师2</span>;老师的年龄29</span>
深克隆例子
<span style="font-size:18px;">package deepclone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* Sdudent,实现序列化
*
*/
public class Student implements Serializable{
private String name;
private int age;
private Teacher teacher;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
//对象流为二进制流,根据对象流进行深克隆
public Student deepClone(){
Student clone = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//将本对象写入输出对象流
oos.writeObject(this);
//再从流里读出来
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
clone = (Student) ois.readObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return clone;
}
}
</span>
<span style="font-size:18px;">package deepclone;
import java.io.Serializable;
public class Teacher implements Serializable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
</span>
<span style="font-size:18px;">package deepclone;
public class TestDeepClone {
public static void main(String[] args) throws CloneNotSupportedException{
Student stu1 = new Student();
stu1.setName("学生1");
stu1.setAge(21);
Teacher teacher = new Teacher();
teacher.setName("老师1");
teacher.setAge(29);
stu1.setTeacher(teacher);
System.out.println("克隆前,原对象: 学生的名字:"+stu1.getName()+";学生的年龄: "+stu1.getAge()+
";老师的名字:"+stu1.getTeacher().getName()+";老师的年龄"+stu1.getTeacher().getAge());
Student clone = stu1.deepClone();
System.out.println("克隆学生的名字:"+clone.getName()+";克隆学生的年龄: "+clone.getAge()+
";克隆老师的名字:"+clone.getTeacher().getName()+";克隆老师的年龄"+clone.getTeacher().getAge());
//修改克隆对象,看是否修改了原对象
clone.setName("学生2");
clone.setTeacher(clone.getTeacher());
//修改克隆老师的名字,查看原学生的老师的名字是否改变
clone.getTeacher().setName("老师2");
System.out.println("克隆后修改克隆对象,原对象: 学生的名字:"+stu1.getName()+";学生的年龄: "+stu1.getAge()+
";老师的名字:"+stu1.getTeacher().getName()+";老师的年龄"+stu1.getTeacher().getAge());
}
}
</span>
运行结果:
<span style="font-size:18px;">克隆前,原对象: 学生的名字:学生1;学生的年龄: 21;老师的名字:老师1;老师的年龄29
克隆学生的名字:学生1;克隆学生的年龄: 21;克隆老师的名字:老师1;克隆老师的年龄29
克隆后修改克隆对象,原对象: 学生的名字:学生1;学生的年龄: 21;老师的名字:老师1;老师的年龄29</span>
根据结果可以看出,修改克隆学生的名字和克隆学生的老师的名字,原学生的名字和原学生的老师的名字没有改变,则说明深复制对所有是基本型变量还是引用型对象都复制了一份新的对象,则修改克隆学生的所有信息不会导致原有学生的所有信息发生变化。