在前面的几讲解内容中,我们学习了线程的一些知识,现在我们在回过头去继续学习I/O的内容,主要是对象的深克隆和浅克隆,也叫对象的深赋值和浅赋值。这也是比较重要的内容。
1. 深拷贝(deep clone) 与 浅拷贝(shallow clone)
浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。如图105-1所示
图105-1:浅复制
深复制(深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的目标对象所引用的对象都复制了一遍。图105-2:深复制
图105-2:深复制
2. Java的clone()方法【定义在Object类中】,查看JDK 文档中Object类里面的clone()方法。
1) clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:
① 对任何的对象x,都有x.clone() !=x [克隆对象与原对象不是同一个对象]
② 对任何的对象x,都有x.clone().getClass()= =x.getClass() [克隆对象与原对象的类型一样]
③ 如果对象x的equals()方法定义恰当,良好(就是没有去改写equals()方法),那么x.clone().equals(x)应该成立。
2) Java中如何进行对象的克隆
① 为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
② 在派生类中覆盖基类的clone()方法,并声明为 public [Object类中的clone()方法为protected的]。(注意这边必须声明为public,不然其他包中就没法对其进行引用了。)
③ 在派生类的clone()方法中,调用super.clone()。[调用父类的clone()方法主要让父类去完成一些特定类型识别的工作,然后重写的子类可以去完成特定当前子类的拷贝动作。]
④ 在派生类中实现Cloneable接口。
查看JDK文档中Cloneable接口,可以发现它里面没有定义任何方法,与Serializable接口定义一样,说明它是一个标示性的接口。编译器看到之后,就知道这种约定,没有去实现这种方法。
3. 说明
① 为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
因为在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
② 继承自java.lang.Object类的clone()方法是浅复制。[也就是如果使用clone()进行对象的克隆的话,我们仅仅是使用了浅克隆(浅复制)而没有真正实现深克隆(深复制)]
4. 程序demo:浅复制
/* * 浅克隆 */ public class CloneTest1 { public static void main(String[] args) throws Exception { Student student = new Student(); student.setAge(20); student.setName("zhangsan"); Student student2 = (Student)student.clone(); System.out.println(student2.getAge()); System.out.println(student2.getName()); } } class Student implements Cloneable{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { //这边克隆的是Student的这个对象 Object object = super.clone(); return object; } }编译执行结果:
20
zhangsan
【说明】:克隆对象成功,现在继续对上面程序的main()方法进行修改,修改如下:
Student student = new Student(); student.setAge(20); student.setName("zhangsan"); Student student2 = (Student)student.clone(); System.out.println(student2.getAge()); System.out.println(student2.getName()); System.out.println("------------------------"); student2.setName("lisi"); System.out.println(student.getName()); System.out.println(student2.getName());编译执行结果:
20
zhangsan
------------------------
zhangsan
lisi
【说明】:Object类的clone()是属于浅复制,程序状况如下图所示:
从这边也可以总结出,student 与 student1 不是同一个对象。
2. 继续写一个复杂的程序,程序如下所示:
public class CloneTest2 { public static void main(String[] args) throws Exception { Teacher teacher = new Teacher(); teacher.setAge(40); teacher.setName("Teacher zhang"); Student2 s1 = new Student2(); s1.setAge(20); s1.setName("zhangsan"); s1.setTeacher(teacher); Student2 s2 = (Student2)s1.clone(); System.out.println(s2.getName()); System.out.println(s2.getAge()); System.out.println(s2.getTeacher().getAge()); System.out.println(s2.getTeacher().getName()); } } class Teacher implements Cloneable{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Student2 implements Cloneable{ private int age; private String name; private Teacher teacher; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } /* * 复制student2对象 * 重写的子类的访问修饰符可比父类的要大。 */ @Override public Object clone() throws CloneNotSupportedException { Object object = super.clone(); return object; } }编译执行结果:
zhangsan
20
40
Teacher zhang
继续修改上述程序中的main()方法的内容
public static void main(String[] args) throws Exception { Teacher teacher = new Teacher(); teacher.setAge(40); teacher.setName("Teacher zhang"); Student2 s1 = new Student2(); s1.setAge(20); s1.setName("zhangsan"); s1.setTeacher(teacher); Student2 s2 = (Student2)s1.clone(); System.out.println(s2.getName()); System.out.println(s2.getAge()); teacher.setName("Teacher Li"); System.out.println(s2.getTeacher().getAge()); System.out.println(s2.getTeacher().getName()); }编译执行结果:
zhangsan
20
40
Teacher Li
【说明】:究其原因是因为clone这个对象是浅复制的概念,被引用的对象是没有进行复制的,只有原生数据类型才会进行真正的复制。
3.修改这个程序,使得它可以进行深复制
单纯的复制Student肯定是有不行,我们必须复制了Student之后,还要继续复制Teacher里面的内容
public class CloneTest2 { public static void main(String[] args) throws Exception { Teacher teacher = new Teacher(); teacher.setAge(40); teacher.setName("Teacher zhang"); Student2 s1 = new Student2(); s1.setAge(20); s1.setName("zhangsan"); s1.setTeacher(teacher); Student2 s2 = (Student2)s1.clone(); System.out.println(s2.getName()); System.out.println(s2.getAge()); teacher.setName("Teacher Li"); System.out.println(s2.getTeacher().getAge()); System.out.println(s2.getTeacher().getName()); } } class Teacher implements Cloneable{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } //赋值Teacher当前的两个对象,age和name @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } class Student2 implements Cloneable{ private int age; private String name; private Teacher teacher; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } /* * 复制student2对象 * 重写的子类的访问修饰符可比父类的要大。 */ @Override public Object clone() throws CloneNotSupportedException { Student2 student2 = (Student2)super.clone(); //再复制一个Teacher student2.setTeacher((Teacher)student2.getTeacher().clone()); return student2; } }编译执行结果:zhangsan
20
40
Teacher zhang
【说明】: