Clone
对象的克隆
在谈本次内容前,先来聊聊什么是克隆。
生物学上的克隆,是把某个生物基因完全提取,利用此基因就能复制出一个与原生物生理结构一致的新的生命,这个新的生命是个实体,它具备原生物的所有特性。
而在面向对象中,假如有一个对象,现在我想要一个与原对象一摸一样的对象当作备胎,那么在堆内存中一步到位得new一个与对象一摸一样对象,就是对象的克隆,也就是对象的复制。
我们在使用基本数据类型的对象时,可以直接用,也不需要new,假如我需要两个boolean类型的true,我们直接声明个变量,把值赋值给该变量就可以了。
但是当我们想要两个一模一样的引用数据类型的对象时,我们栈中变量中存放的只是在堆中对象的地址,那么我直接复制的话其实就是复制个引用,对象还是那一个。
实现克隆
需要克隆的类需要实现Cloneable
接口, 这就像提取基因一样;
还需要重写clone()
方法,需要把super的类型转为克隆的类型; 这个方法来自Object,如果你不去重写当然也可以,只是它的类型为Object类型,不方便使用;
需要克隆的对象使用clone()
方法,就能返回一个跟原对象一样的对象。
public class Achievement implements Cloneable { //1.实现Cloneable接口
private double Chinese;
private double math;
private double English;
//clone()方法需要抛出一个CloneNotSupportedException
@Override
protected Achievement clone() throws CloneNotSupportedException {
return (Achievement)super.clone();//2.重写clone()方法
}
public Achievement() {
}
public Achievement(double chinese, double math, double english) {
Chinese = chinese;
this.math = math;
English = english;
}
@Override
public String toString() {
return "Achievement{" +
"Chinese=" + Chinese +
", math=" + math +
", English=" + English +
'}';
}
public double getChinese() {
return Chinese;
}
public void setChinese(double chinese) {
Chinese = chinese;
}
public double getMath() {
return math;
}
public void setMath(double math) {
this.math = math;
}
public double getEnglish() {
return English;
}
public void setEnglish(double english) {
English = english;
}
}
public class TestAchievement {
public static void main(String[] args) throws CloneNotSupportedException {
Achievement achievement = new Achievement(90, 85, 61);
Achievement achievement1 = achievement.clone();//3.使用clone()获取克隆的对象
System.out.println(achievement1);
//克隆的对象与原对象不再是同一个对象
System.out.println(achievement == achievement1);//false
}
}
浅克隆与深克隆
对象深克隆:
需要注意,被克隆的对象中所引用的对象若想也被克隆,那么被引用的这个也需要被克隆比如克隆一个集合以及集合中包含的对象
对象潜克隆:
仅仅复制对象本身,包括基本数据类型,但是被引用的对象不会被复制
public class Student implements Cloneable {
private String name;
private Achievement achievement;
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
Achievement achievement = (Achievement) student.getAchievement().clone();
student.setAchievement(achievement);
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", achievement=" + achievement +
'}';
}
public Achievement getAchievement() {
return achievement;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(String name, Achievement achievement) {
this.name = name;
this.achievement = achievement;
}
}
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Achievement achievement = new Achievement(100, 100, 100);
Student student1 = new Student("小王", achievement);
//克隆一个学生
Student student2 = (Student) student1.clone();
//克隆后两个学生的信息都一致
System.out.println(student1);
System.out.println(student2);
//查看两个对象是否是同一对象
System.out.println(student1 == student2);
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
//修改一个学生的信息后观察
student2.setName("小李");
student2.getAchievement().setChinese(60);
student2.getAchievement().setEnglish(80);
System.out.println(student1);
System.out.println(student2);
}
}
克隆一个集合以及集合中包含的对象
其实集合已经帮我们重写好了clone()方法,我们直接使用就行了。
拿ArrayList举例:
它的源码如下:
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
克隆集合的使用:
public static void main(String[] args) throws CloneNotSupportedException {
Achievement achievement = new Achievement(90, 85, 61);
ArrayList<Achievement> achievements = new ArrayList<>();
achievements.add(achievement);
ArrayList<Achievement> clone = (ArrayList<Achievement>) achievements.clone();
clone.forEach(e-> System.out.println(e));
}