深克隆与浅克隆
一个对象的浅克隆是指,创建一个新对象,这个对象拥有原对象所有字段的一个副本。这样的浅克隆往往会造成一些问题。比如,当一个对象的某个字段,是另外一个可变对象的引用时,浅克隆仅仅为克隆对象复制了该子对象的引用,也就是说,克隆对象与原对象共享这个子对象。
如上图所示,初始对象origin和克隆对象copy的hireDay字段指向同一个Date对象。
而对于深拷贝而言,要求一并深拷贝所有引用类型的字段,确保克隆对象和原对象不共享任何字段。这个过程可能是递归的,因为子对象也有可能拥有引用类型的字段。
在Java的Object类中定义了一个protected的方法clone,
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
该方法被native修饰,表名该方法是由其他语言实现的。
他采用的是简单的逐字段拷贝,也就是浅拷贝。并且,受限于protected修饰符,clone方法并不能在你所定义的Object的子类外使用,关于protected修饰符的作用,请参见我的另一篇博客:java权限修饰符( protected ):什么叫对包和子类可见
故若要向外提供一个clone方法,你必须要实现Cloneable接口,重写clone方法。
Cloneable接口
Cloneable接口是一个空接口,,也叫做标记接口。标记接口不包含任何方法;它的唯一作用是允许在类型查询中使用instanceof。如if(obj isinstanceof Cloneable)...
用于判断目标对象是否是可克隆的。
需要注意的是,重写Object类的clone方法,必须实现Cloneable接口,不然的话会在程序运行时抛出CloneNotSupportedException,尽管该接口什么方法也没有
所有的数组类型都有一个公共的clone方法,且不是protected的,这意味着你可以在任何地方调用该方法。该方法将返回一个新数组,包含原数组中的所有元素的副本,它同样是浅复制的。
示例:
public class Main {
public static void main(String[] args){
var fs1 = new Father[]{new Father(),new Father()};
var fs2 = fs1.clone();
System.out.println(fs1==fs2);
System.out.println(fs1[0]==fs2[0]);
}
}
结果: