是什么
克隆是创建对象的副本的过程。在Java中,对象克隆是通过clone()
方法来实现的。 克隆可以帮助我们创建对象的副本,而不必重新构造或初始化对象。
原理
在堆内存中新开辟一段空间,然后把被克隆对象的属性和方法赋值一份到新开辟的空间里面(副本)。 clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建。 对于引用数据类型而言,只是复制了原对象引用的地址,并没有开辟新的空间,新对象空间里面的引用和源对象的里面的引用都指向于同一个空间
什么时候用
- 创建多个且大部分属性相同的对象时,可以考虑使用克隆。
- 当需要创建对象的副本,并且希望该副本与原始对象相互独立,即对副本对象的修改不会影响到原始对象时,可以考虑使用克隆。
克隆的分类
浅拷贝
浅拷贝是指创建一个新对象,然后将原始对象的字段复制到新对象中。如果字段是基本数据类型,则复制其值;如果字段是引用类型,则复制其引用,而不是复制引用指向的对象本身。
示例
两个测试类
/**
* 英雄类,因为需要用克隆,需要实现Cloneable接口
*/
public class Hero implements Cloneable {
/** 名字 */
private String name;
/** 武器 */
private Weapon weapon;
...
@Override
public Hero clone(){
Hero hero=null;
try {
hero=(Hero)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return hero;
}
}
/**
* 武器类实现Cloneable接口
*/
public class Weapon implements Cloneable{
/** 名字 */
private String name;
/** 耐久值 */
private int durable;
...
@Override
public Weapon clone() {
Weapon weapon=null;
try {
weapon=(Weapon)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return weapon;
}
}
场景
/**
* 场景类,用于演示
*/
public class Scene {
public static void main(String[] args) {
Hero hero=new Hero("亚瑟",new Weapon("屠龙",100));
Hero copyHero=hero.clone();
// debug-1
copyHero.setName("阿古多");
// 重新设置武器耐久,注意这里设置的是阿古多(copyHero)的武器,而不是亚瑟(hero)的武器
copyHero.getWeapon().setDurable(467);
// debug-2
System.out.println(hero);
System.out.println(copyHero);
}
}
结果
Hero{name='亚瑟', weapon=Weapon{name='屠龙', durable=467}}
Hero{name='阿古多', weapon=Weapon{name='屠龙', durable=467}}
debug-1,copy对象后 debug-2,设置名字和武器耐久后
可见,“阿古多”的武器还是引用自“亚瑟”的武器,导致在更改耐久后,“亚瑟”的武器也跟着发生改变。
深拷贝
深拷贝是指创建一个新对象,并且递归地复制原始对象的所有字段,包括引用类型字段所指向的对象。这样,原始对象和副本对象之间的所有对象都是独立的,修改副本对象不会影响到原始对象。
示例
Hero重写copy(),Weapon不变,场景类不变
public class Hero implements Cloneable {
...
@Override
public Hero clone() {
Hero hero = null;
try {
hero = (Hero) super.clone();
// 给weapon属性也给克隆一下
setWeapon(getWeapon().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return hero;
}
}
结果
Hero{name='亚瑟', weapon=Weapon{name='屠龙', durable=100}}
Hero{name='阿古多', weapon=Weapon{name='屠龙', durable=467}}
debug-1,copy对象后 debug-2,设置名字和武器耐久后
要点就在于在copy Hero的时候把Weapon也copy了一下。这样两边互不影响
注意
- 被克隆的类必须实现Cloneable接口,并且重写clone()方法。
- 在重写clone()方法时,需要处理可能抛出的CloneNotSupportedException异常。
- 在使用浅拷贝时,需要注意引用类型字段的共享问题,修改副本对象中的引用类型字段可能会影响到原始对象。
- 在使用深拷贝时,需要确保所有对象都支持克隆操作,否则需要手动复制对象的所有字段。
和原型模式的区别
- 克隆是Java语言提供的一种机制,通过clone()方法来创建对象的副本,而原型模式是一种设计模式,用于创建对象的副本。
- 克隆是基于对象的clone()方法,而原型模式是基于类的复制构造函数或者原型工厂方法。
- 在使用克隆时,需要考虑对象的克隆方式(浅拷贝还是深拷贝),而原型模式通常只需要在原型对象中实现复制构造函数或者原型工厂方法即可。