介绍
一直以来只知道 Java 有 clone 方法, 该方法属于 Object 的, 对于什么是浅克隆与深克隆就比较模糊了, 现在就来补充学习一下.
概念
浅拷贝 (浅克隆) 复制出来的对象的所有变量都含有与原来的对象相同的值, 而所有的对其他对象的引用仍然指向原来的对象.
深拷贝 (深克隆) 复制出来的所有变量都含有与原来的对象相同的值, 那些引用其他对象的变量将指向复制出来的新对象, 而不再是原有的那些被引用的对象. 换言之, 深复制把要复制的对象所引用的对象都复制了一遍.
实现
Student.java
- public class Student implements Cloneable {
- private String name;
- private int age;
- private Teacher teacher;
- public Student(String name, int age, Teacher teacher) {
- this.name = name;
- this.age = age;
- this.teacher = teacher;
- }
- }
Teacher.java
- public class Teacher implements Cloneable {
- private String name;
- private String course;
- public Teacher(String name, String course) {
- this.name = name;
- this.course = course;
- }
- }
Student 类中包含有 name,age 和 Teacher 对象.
浅克隆
克隆对象实现 Cloneable 接口 (该接口是一个标记接口), 在克隆的方法里面调用 super.clone(), 就会返回克隆后的对象.
- public class Student implements Cloneable {
- private String name;
- private int age;
- private Teacher teacher;
- public Student(String name, int age, Teacher teacher) {
- this.name = name;
- this.age = age;
- this.teacher = teacher;
- }
- public Student clone() {
- Student student = null;
- try {
- student = (Student) super.clone();
- } catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return student;
- }
- }
测试
- public static void main(String args[]) throws IOException,
- ClassNotFoundException {
- Teacher teacher = new Teacher("王老师", "英语");
- Student student = new Student("小明", 11, teacher);
- Student clone = student.clone();
- clone.setName("小强");
- clone.setAge(20);
- clone.getTeacher().setName("李老师");
- System.out.println(student.getName() + " " + student.getAge());
- System.out.println(clone.getName() + " " + clone.getAge());
- System.out.println(clone.getTeacher() == student.getTeacher());
- }
输出:
小明 11
小强 20
true从上面结果可知, 克隆出来的 Student 对象里的 name 和 age 是新的, 但是 teacher 是和原来的共享的, 这就是浅克隆.
深克隆
Student.java
- public class Student implements Cloneable {
- private String name;
- private int age;
- private Teacher teacher;
- public Student(String name, int age, Teacher teacher) {
- this.name = name;
- this.age = age;
- this.teacher = teacher;
- }
- public Student clone() {
- Student student = null;
- try {
- student = (Student) super.clone();
- Teacher teacher = this.teacher.clone();
- student.setTeacher(teacher);
- } catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return student;
- }
- }
Teacher.java
- public class Teacher implements Cloneable {
- private String name;
- private String course;
- public Teacher(String name, String course) {
- this.name = name;
- this.course = course;
- }
- public Teacher clone() {
- Teacher clone = null;
- try {
- clone = (Teacher) super.clone();
- } catch(CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return clone;
- }
- }
要实现深克隆的话, 克隆对象里面的对象类型也必须实现 Cloneable 接口并调用 clone().
测试:
- public static void main(String args[]) throws IOException,
- ClassNotFoundException {
- Teacher teacher = new Teacher("王老师", "英语");
- Student student = new Student("小明", 11, teacher);
- Student clone = student.clone();
- clone.setName("小强");
- clone.setAge(20);
- clone.getTeacher().setName("李老师");
- System.out.println(student.getName() + " " + student.getAge());
- System.out.println(clone.getName() + " " + clone.getAge());
- System.out.println(clone.getTeacher() == student.getTeacher());
- }
输出:
小明 11
小强 20
false这时, 两个对象的中的 Teacher 就不是同一个对象了, 实现了深克隆, 但是如果要克隆的对象继承链比较长的话要实现深克隆, 就必须逐层地实现 Cloneable, 这个过程是比较麻烦的, 不过还有一种方法可以简便地实现深克隆.
serializable 克隆
大家知道, Java 可以把对象序列化写进一个流里面, 反之也可以把对象从序列化流里面读取出来, 但这一进一出, 这个对象就不再是原来的对象了, 就达到了克隆的要求.
- public class Student implements Serializable {
- private String name;
- private int age;
- private Teacher teacher;
- public Student(String name, int age, Teacher teacher) {
- this.name = name;
- this.age = age;
- this.teacher = teacher;
- }
- public Student serializableClone() throws IOException,
- ClassNotFoundException {
- Student clone;
- ByteArrayOutputStream bo = new ByteArrayOutputStream();
- ObjectOutputStream oo = new ObjectOutputStream(bo);
- oo.writeObject(this);
- ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
- ObjectInputStream oi = new ObjectInputStream(bi);
- clone = (Student) oi.readObject();
- return clone;
- }
- }
通过把对象写进 ByteArrayOutputStream 里, 再把它读取出来. 注意这个过程中所有涉及的对象都必须实现 Serializable 接口, 由于涉及 IO 操作, 这种方式的效率会比前面的低.
来源: http://www.jianshu.com/p/4a2b9e0b6eb0