目录
克隆与浅克隆区别
深浅克隆的区别在于,能否支持引用类型(包括类、接口、数组等)的成员变量的复制。
浅克隆:对象只复制了它本身和其中包含的值类型的成员变量,引用类型的成员对象并没有复制。
深克隆:对象本身以及包含的所有成员变量都会被复制。
浅克隆demo
关键点:Student类,实现Cloneable接口。
Student中数值类型属性 name sex,引用类型teacher
public class Student implements Cloneable {
String name;
String sex;
Teacher teacher;
public Student(String name, String sex, Teacher teacher) {
this.name = name;
this.sex = sex;
this.teacher = teacher;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", teacher=" + teacher +
'}';
}
}
/**
* 教师类
*/
public class Teacher {
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public Teacher(String name, String sex) {
this.name = name;
this.sex = sex;
}
}
测试类:
public class LightCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("laoshi", "男");
Student a = new Student("zhangsan", "男", teacher);
Student b = (Student) a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a.equals(b));
System.out.println("b为a克隆的一个对象,两者引用不相等");
System.out.println("对克隆的的值属性修改,a的性别属性修改为女");
b.setSex("女");
System.out.println(a);
System.out.println(b);
System.out.println("对克隆的的值属性修改,被克隆的对象中对应值属性无变化了");
// 对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
System.out.println("对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi");
a.getTeacher().setName("laosi");
System.out.println(a);
System.out.println(b);
System.out.println("对被克隆的的对象进行属性修改,克隆的对象中对应属性也变化了");
// 对b的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
System.out.println("对克隆的的对象进行属性修改,将teacher的name属性由改为laowu\"");
b.getTeacher().setName("laowu");
System.out.println(a);
System.out.println(b);
System.out.println("对克隆的的对象进行属性修改,被克隆的对象中对应属性也变化了");
}
}
运行结果:
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b为a克隆的一个对象,两者引用不相等
对克隆的的值属性修改,a的性别属性修改为女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对克隆的的值属性修改,被克隆的对象中对应值属性无变化了
对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laosi', sex='男'}}
对被克隆的的对象进行属性修改,克隆的对象中对应属性也变化了
对克隆的的对象进行属性修改,将teacher的name属性由改为laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laowu', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
对克隆的的对象进行属性修改,被克隆的对象中对应属性也变化了
可以看出:对象只复制了它本身和其中包含的值类型的成员变量,修改克隆对象的值属性,不会影响被克隆对象属性的值,但修改引用会影响被克隆的引用属性。
深克隆demo
关键点:Student类,实现Cloneable接口 重写clone方法,引用属性teacher类也需要实现Cloneable接口。
如:Student的重写clone方法:
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student)super.clone();
student.setTeacher((Teacher)student.getTeacher().clone());
return student;
}
引用属性teacher类需要实现Cloneable接口。
测试类:
public class DeepCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("laoshi", "男");
Student a = new Student("zhangsan", "男", teacher);
Student b = (Student) a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a.equals(b));
System.out.println("b为a克隆的一个对象,两者引用不相等");
System.out.println("对克隆的的值属性修改,a的性别属性修改为女");
b.setSex("女");
System.out.println(a);
System.out.println(b);
System.out.println("对克隆的的值属性修改,被克隆的对象中对应值属性未变化");
// 对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
System.out.println("对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi");
a.getTeacher().setName("laosi");
System.out.println(a);
System.out.println(b);
System.out.println("对被克隆的的对象进行属性修改,克隆的对象中对应属性未变化");
// 对b的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
System.out.println("对克隆的的对象进行属性修改,将teacher的name属性由改为laowu\"");
b.getTeacher().setName("laowu");
System.out.println(a);
System.out.println(b);
System.out.println("对克隆的的对象进行属性修改,被克隆的对象中对应属性未变化");
}
}
运行结果:
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
false
b为a克隆的一个对象,两者引用不相等
对克隆的的值属性修改,a的性别属性修改为女
Student{name='zhangsan', sex='男', teacher=Teacher{name='laoshi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对克隆的的值属性修改,被克隆的对象中对应值属性未变化
对a的引用类型成员变量teacher进行修改,将teacher的name属性由laoshi改为laosi
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laoshi', sex='男'}}
对被克隆的的对象进行属性修改,克隆的对象中对应属性未变化
对克隆的的对象进行属性修改,将teacher的name属性由改为laowu"
Student{name='zhangsan', sex='男', teacher=Teacher{name='laosi', sex='男'}}
Student{name='zhangsan', sex='女', teacher=Teacher{name='laowu', sex='男'}}
对克隆的的对象进行属性修改,被克隆的对象中对应属性未变化
可以看到手动实现clone方法对引用对象进行克隆,如果引用对象还有引用类型属性,那么只能继续低引用对象进行clone实现。
那么有没有更好的方法实现深度克隆呢?
当然有,使用序列化,之需要将Student和Teacher类都实现Serializable接口,Student重写clone方法。
如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Student implements Serializable {
String name;
String sex;
Teacher teacher;
public Student(String name, String sex, Teacher teacher) {
this.name = name;
this.sex = sex;
this.teacher = teacher;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public Object clone() throws CloneNotSupportedException {
Student student = 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);
student = (Student) ois.readObject();
}catch (Exception e){
System.out.println(e.toString());
}
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", teacher=" + teacher +
'}';
}
}
import java.io.Serializable;
/**
* 教师类
*/
public class Teacher implements Serializable {
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public Teacher(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
使用Serializable接口序列化,引用对象也必须实现Serializable接口。
文末思考
除了使用Serializable接口序列化实现深克隆,还有其他方法吗?
提示:使用使用Json。