java的克隆类似于对象的复制。
为什么需要克隆想对一个对象进行处理,且保留原有的对象。
比如根据一个叫张三的学生信息,复制出李四、王五的学生信息,且保留张三的学生信息,然后组装三个学生的List。
java克隆分为浅克隆与深克隆。
- 浅克隆:只克隆基本类型。引用类型没有克隆,只是新建一个引用,该引用仍然指向原有的对象。比如张三的学生信息中,有老师这个引用对象。克隆张三的学生信息修改成李四的信息后,老师信息并未被克隆,修改张三的老师,李四的老师也被修改。
- 深克隆:基本类型和引用类型都被克隆,引用类型的变量指向的是新克隆的对象。
下面上代码实例,为了保险起见,咱们上三层嵌套。Student里有Teacher,Teacher里有Course。
浅克隆- Student代码
package org.ludk.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 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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", teacher=" + teacher +
'}';
}
}
- Teacher代码
package org.ludk.clone;
public class Teacher implements Cloneable{
private String name;
private int age;
private Course course;
public Teacher(String name, int age, Course course) {
this.name = name;
this.age = age;
this.course = course;
}
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 Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + ''' +
", age=" + age +
", course=" + course +
'}';
}
}
- Course代码
package org.ludk.clone;
public class Course {
private String name;
private int days;
public Course(String name, int days) {
this.name = name;
this.days = days;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Course{" +
"name='" + name + ''' +
", days=" + days +
'}';
}
}
- 测试类代码
package org.ludk.clone;
public class TestClone implements Cloneable{
public static void main(String[] args) throws CloneNotSupportedException {
//新建个学生张三
Student student=new Student("张三",24,new Teacher("王老师",40,new Course("英语",90)));
//克隆学生
Student cloneStudent=(Student) student.clone();
System.out.println("学生张三和克隆学生是否为一个老师");
System.out.println(student.getTeacher()==cloneStudent.getTeacher());
System.out.println("学生张三和克隆学生是否为一个课程");
System.out.println(student.getTeacher().getCourse()==cloneStudent.getTeacher().getCourse());
System.out.println("学生张三的信息");
System.out.println(student);
//克隆学生的信息
System.out.println("克隆学生的信息");
System.out.println(cloneStudent);
}
}
- 测试类执行结果
D:workjavabinjava.exe "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3libidea_rt.jar=62957:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3bin" -Dfile.encoding=UTF-8 -classpath D:workjavajrelibcharsets.jar;D:workjavajrelibdeploy.jar;D:workjavajrelibextaccess-bridge-64.jar;D:workjavajrelibextcldrdata.jar;D:workjavajrelibextdnsns.jar;D:workjavajrelibextjaccess.jar;D:workjavajrelibextjfxrt.jar;D:workjavajrelibextlocaledata.jar;D:workjavajrelibextnashorn.jar;D:workjavajrelibextsunec.jar;D:workjavajrelibextsunjce_provider.jar;D:workjavajrelibextsunmscapi.jar;D:workjavajrelibextsunpkcs11.jar;D:workjavajrelibextzipfs.jar;D:workjavajrelibjavaws.jar;D:workjavajrelibjce.jar;D:workjavajrelibjfr.jar;D:workjavajrelibjfxswt.jar;D:workjavajrelibjsse.jar;D:workjavajrelibmanagement-agent.jar;D:workjavajrelibplugin.jar;D:workjavajrelibresources.jar;D:workjavajrelibrt.jar;D:workspringcloud-demotargetclasses org.ludk.clone.TestClone
学生张三和克隆学生是否为一个老师
true
学生张三的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
克隆学生的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
Process finished with exit code 0
从上面的例子,可以看到学生信息被复制了,但是里面的Teacher信息仍然是一个,仍然是一个对象。
深克隆深克隆有两种方法。一种是遇到属性为引用类型,则对该引用类型对象进行复制。另一种是用序列化实现深克隆。
第一种方法性能高些,但是实现起来复杂,因为对象里可能还嵌套对象。第二种方法实现起来简单,但是性能差些。
自定义深克隆- 修改Student的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Student cloneStudent= (Student)super.clone();
cloneStudent.setTeacher((Teacher) this.getTeacher().clone());
return cloneStudent;
}
- 再次跑测试类的结果
D:workjavabinjava.exe "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3libidea_rt.jar=50882:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3bin" -Dfile.encoding=UTF-8 -classpath D:workjavajrelibcharsets.jar;D:workjavajrelibdeploy.jar;D:workjavajrelibextaccess-bridge-64.jar;D:workjavajrelibextcldrdata.jar;D:workjavajrelibextdnsns.jar;D:workjavajrelibextjaccess.jar;D:workjavajrelibextjfxrt.jar;D:workjavajrelibextlocaledata.jar;D:workjavajrelibextnashorn.jar;D:workjavajrelibextsunec.jar;D:workjavajrelibextsunjce_provider.jar;D:workjavajrelibextsunmscapi.jar;D:workjavajrelibextsunpkcs11.jar;D:workjavajrelibextzipfs.jar;D:workjavajrelibjavaws.jar;D:workjavajrelibjce.jar;D:workjavajrelibjfr.jar;D:workjavajrelibjfxswt.jar;D:workjavajrelibjsse.jar;D:workjavajrelibmanagement-agent.jar;D:workjavajrelibplugin.jar;D:workjavajrelibresources.jar;D:workjavajrelibrt.jar;D:workspringcloud-demotargetclasses org.ludk.clone.TestClone
学生张三和克隆学生是否为一个老师
false
学生张三的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
克隆学生的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
Process finished with exit code 0
其实上面的自定义深克隆方法只是深克隆了老师的信息,课程的信息依然为浅克隆。可以自己实现,继续扩展。
java序列化实现深克隆- Teacer、Student、Course都得实现java.io.Serializable接口
- 修改Student的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Student cloneObj = null;
try {
// 写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(this);
obs.close();
// 分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
// 返回生成的新对象
cloneObj = (Student) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
- 重新跑测试类的结果
D:workjavabinjava.exe "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3libidea_rt.jar=55660:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.3bin" -Dfile.encoding=UTF-8 -classpath D:workjavajrelibcharsets.jar;D:workjavajrelibdeploy.jar;D:workjavajrelibextaccess-bridge-64.jar;D:workjavajrelibextcldrdata.jar;D:workjavajrelibextdnsns.jar;D:workjavajrelibextjaccess.jar;D:workjavajrelibextjfxrt.jar;D:workjavajrelibextlocaledata.jar;D:workjavajrelibextnashorn.jar;D:workjavajrelibextsunec.jar;D:workjavajrelibextsunjce_provider.jar;D:workjavajrelibextsunmscapi.jar;D:workjavajrelibextsunpkcs11.jar;D:workjavajrelibextzipfs.jar;D:workjavajrelibjavaws.jar;D:workjavajrelibjce.jar;D:workjavajrelibjfr.jar;D:workjavajrelibjfxswt.jar;D:workjavajrelibjsse.jar;D:workjavajrelibmanagement-agent.jar;D:workjavajrelibplugin.jar;D:workjavajrelibresources.jar;D:workjavajrelibrt.jar;D:workspringcloud-demotargetclasses org.ludk.clone.TestClone
学生张三和克隆学生是否为一个老师
false
学生张三和克隆学生是否为一个课程
false
学生张三的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
克隆学生的信息
Student{name='张三', age=24, teacher=Teacher{name='王老师', age=40, course=Course{name='英语', days=90}}}
Process finished with exit code 0
从上面的例子可以看到,java序列化实现深克隆,那是真的深克隆。
参考下面是引用https://www.cnblogs.com/xzwblog/p/7230788.html博客的图片
- 浅克隆
- 深克隆



