为什么要克隆
首先思考一个问题, 为什么需要克隆对象? 直接new一个对象不行吗?
克隆的对象可能包含一些已经修改过的属性, 而new出来的对象的属性都还是初始化时候的值, 所以当需要一个新的对象来保存当前对象的"状态"时就要靠克隆了.
当然, 把对象的属性一个一个的赋值给新new的对象也是可以的, 但是这样一来麻烦不说, 二来, 我们通过源码查看 Object的clone方法是一个native方法(native方法是非Java语言实现的代码, 供Java程序调用, 要想访问比较底层的与操作系统相关的就没办法了, 只能由靠近操作系统的语言来实现), 就是快啊, 实在更底层实现的.
我们常见的 Object a = new Object(); Object b; b = a; 这种形式的代码复制的是引用, 即对象在内存中的地址, a和b指向了同一个对象. 而通过clone方法赋值的对象跟原来的对象是同时独立存在的.
概念
浅克隆:被克隆的对象里的所有变量值都与原来的对象相同, 而所有对其他对象的引用仍然指向原来的对象. 简单说, 浅克隆仅克隆当前对象, 而不克隆当前对象所引用的对象.
深克隆:被克隆的对象里的所有变量值都与原来的对象相同, 那些引用其他对象的变量将指向被复制过的新对象, 而不再是原来被引用的对象. 简单说, 深克隆不仅克隆了当前对象, 还把当前对象所引用的对象都复制了一遍.
图片来源:https://blog.csdn.net/lovezhaohaimig/article/details/80372233
Object中的clone
Object类中的clone()方法属于浅克隆. 它的工作原理如下: 在内存中先开辟一块和原始对象相同的空间, 然后复制原始对象的内容. 对于基本数据类型, 这样操作当然没问题, 但对于引用类型, 由于保存的仅仅是对象的引用, 克隆过去的引用所指向的是同一个对象.
Java中实现浅克隆
java中实现clone要实现 Cloneable 接口, 该接口十分简单, 仅仅起到一个标识的作用,源码如下:
浅克隆案例
student类:
package com.dgut.ssmDemo.service.impl;
/**
* @author jcH
* @create 2020-03-11 13:33
*/
public class Student{
private String stuName;
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
public String toString() {
return "Student{" +
"name='" + stuName + '\'' +
'}';
}
}
Teacher类:
package com.dgut.ssmDemo.service.impl;
/**
* @author jcH
* @create 2020-03-11 13:34
*/
public class Teacher implements Cloneable{
private String teaName;
private Student student;
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher{" +
"teaName='" + teaName + '\'' +
", student=" + student +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
//浅拷贝
return super.clone();
}
}
测试:
package com.dgut.ssmDemo.service.impl;
/**
* @author jcH
* @create 2020-03-11 13:36
*/
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setStuName("张三");
Teacher teacher1 = new Teacher();
teacher1.setTeaName("英语老师");
teacher1.setStudent(student);
Teacher teacher2 = (Teacher) teacher1.clone();
teacher2.setTeaName("语文老师");
teacher2.getStudent().setStuName("李四");
System.out.println(teacher1);
System.out.println(teacher2);
}
}
输出结果:
Java中实现深克隆
将类中的所有引用类型都进行clone, 并重写对象clone()方法, 对所有引用类型进行clone.
与浅克隆相比,需要:1、让Student类实现Cloneable接口;2、重写Teacher类中的clone方法:
@Override
protected Object clone() throws CloneNotSupportedException {
//深拷贝
Teacher teacher = (Teacher) super.clone();
teacher.student = (Student) student.clone();
return teacher;
}
输出结果:
序列化和反序列化实现深克隆
在引用数据类型很多的情况下可以使用序列化和反序列化实现深克隆,但不推荐这么做,因为会消耗大量的CPU。
如何利用序列化来完成对象的拷贝呢?在内存中通过字节流的拷贝是比较容易实现的。把母对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与母对象之间并不存在引用共享的问题,真正实现对象的深拷贝。
代码可以参考下图,前提是所操作的类需要实现Serializable接口