概述
clone方法位于Object中,该方法用于创建并返回此对象的一个副本。Object类是所有Java类的父类,因此所有的Java类都继承了clone方法,该方法的访问控制修饰符为protected
。
需要注意的是,想要调用该方法,被复制的对象的类必须实现java.lang.Cloneable
接口。如果没有实现Cloneable接口的类的实例调用了clone方法,则会在运行时抛出CloneNotSupportedException
异常。
Cloneable接口中没有声明任何方法,只是用来标记“可以使用clone方法进行复制”的,这样的接口也被称为标记接口(marker interface)。
clone方法内部所进行的处理是分配与要复制的实例同样大小的内存空间,接着将要复制的实例中的字段的值复制到所分配的内存空间中去。
示例
Book.java、测试方法
public class Book implements Cloneable {
private String name;
private List<String> dataList = new ArrayList<>();
public Book(String name, List<String> dataList) {
System.out.println("Book --> Constructor");
this.name = name;
this.dataList = dataList;
}
public Book createClone() {
Book book = null;
try {
book = (Book) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return book;
}
// set get略
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", dataList=" + dataList +
'}';
}
}
@Test
public void test() {
String name = "Java";
List<String> dataList = Lists.newArrayList("Object", "Clone");
Book book = new Book(name, dataList);
System.out.println(book);
System.out.println("book => "+Integer.toHexString(book.hashCode()));
System.out.println("book data => "+Integer.toHexString(book.getDataList().hashCode()));
System.out.println("------------------------------------");
Book clone = book.createClone();
System.out.println(clone);
System.out.println("clone => "+Integer.toHexString(clone.hashCode()));
System.out.println("clone data => "+Integer.toHexString(clone.getDataList().hashCode()));
dataList.clear();
System.out.println("book data => "+book.getDataList());
System.out.println("clone data => "+clone.getDataList());
}
输出结果
Book --> Constructor
Book{name='Java', dataList=[Object, Clone]}
book => 67b64c45
book data => 42f4d7f
------------------------------------
Book{name='Java', dataList=[Object, Clone]}
clone => 4411d970
clone data => 42f4d7f
book data => []
clone data => []
对比分隔符前后,以及调用clear方法后的结果,可以发现
- clone方法并不会调用被复制实例的构造方法
- clone方法复制的只是引用
浅拷贝
clone方法所进行的复制只是将被复制的实例中的字段值直接复制到新的实例中。
比如字段中保存的是数组(或者集合等等引用类型)时,则只会复制该数组的引用,并不会一一复制数组中的元素。这样字段对字段的复制(field-to-field-copy)被称为浅拷贝(shallow copy)。
因为clone方法不会调用被复制实例的构造方法,所以对于在构造方法中有特殊处理的类,或是想要实现深拷贝(deep clone)时就需要由我们自己来实现,通常的形式就是在调用super.clone()
之后做自己的处理。