克隆就是复制一个对象的副本,但一个对象中有可能有基本数据类型,如 int,long,float等,也可能含有对象引用数据类型,如 数组,集合等
浅克隆:
被复制对象的所有变量都含有与原来的对象相同的值,而所有的其他对象的引用仍然指向原来的对象。换言之,浅克隆仅仅复制所考虑的对象,而不复制它所引用的对象。
深克隆:
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
克隆方法clone()
clone是定义一个Object类下基本方法之一,任何克隆的过程都将实现clone()方法,而其在Object接口中定义如下:
protected native Object clone() throws CloneNotSupportedException;
通常克隆对象都是通过调用super.clone()方法来获取克隆对象的,所以任何克隆的过程最终都将到达java.lang.Object的clone()方法。但是在覆写clone()方法时,这个类需要实现Clonable接口,这个接口中没有定义方法,只做为一种标识存在。如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了super.Clone() 方法),那么Object的clone()方法就会抛出CloneNotSupportedException 异常。
例子:
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;
}
/**
* 省略 setX、getX 方法
*/
}
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;
}
/**
* 省略 setX、getX 方法
*/
}
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;
}
/**
* 实现clone方法
*/
public Student clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
测试:
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;
}
/**
* 实现clone方法
*/
public Student clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
public static void main(String[] args) {
Teacher teacher = new Teacher("胡老师", "100");
Student student = new Student("张三", 27, teacher);
Student stu2 = student.clone();
stu2.setName("李四");
stu2.setAge(28);
stu2.getTeacher().setName("刘老师");
System.out.println("克隆前:" + student.getName() + "," + student.getAge() + ","
+ student.getTeacher().getName());
System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + ","
+ stu2.getTeacher().getName());
System.out.println(student.getTeacher() == stu2.getTeacher());
}
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;
}
}
输出:
克隆前:张三,27,刘老师
克隆后:李四,28,刘老师
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;
}
/**
* 实现clone方法
*/
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;
}
/**
* 省略 setX、getX 方法
*/
}
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;
}
/**
* 省略 setX、getX 方法
*/
}
要实现深克隆的话,克隆对象里面的对象类型也必须实现Cloneable接口并调用clone()。
测试
public static void main(String[] args) {
Teacher teacher = new Teacher("胡老师", "100");
Student student = new Student("张三", 27, teacher);
Student stu2 = student.clone();
stu2.setName("李四");
stu2.setAge(28);
stu2.getTeacher().setName("刘老师");
System.out.println("克隆前:" + student.getName() + "," + student.getAge() + ","
+ student.getTeacher().getName());
System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + ","
+ stu2.getTeacher().getName());
System.out.println(student.getTeacher() == stu2.getTeacher());
}
输出结果:
克隆前:张三,27,胡老师
克隆后:李四,28,刘老师
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;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Teacher teacher = new Teacher("胡老师", "100");
Student student = new Student("张三", 27, teacher);
Student stu2 = student.serializableClone();
stu2.setName("李四");
stu2.setAge(28);
stu2.getTeacher().setName("刘老师");
System.out.println("克隆前:" + student.getName() + "," + student.getAge()
+ "," + student.getTeacher().getName());
System.out.println("克隆后:" + stu2.getName() + "," + stu2.getAge() + ","
+ stu2.getTeacher().getName());
System.out.println(student.getTeacher() == stu2.getTeacher());
}
/**
* 省略 setX、getX 方法
*/
}
Teacher.java也要实现Serializable接口