浅拷贝(shallow copy):只负责克隆按值传递的数据(比如基本数据类型、String类型)
深拷贝(deep copy):除了shallow copy的值外,还负责克隆引用类型的数据,基本是就是被克隆实例所有的属性数据都会被克隆出来。
这里推荐一篇介绍非常详细的外文文章
什么是克隆?
克隆就是在内存中拷贝一份已有对象的过程。Java中java.lang.Object类中的方法clone()就是作为克隆用的。这个方法会把实例对象逐个字段的值原样拷贝过来,对于引用类型则是把对应的内存地址拷贝过来。不过不是所有类都能使用克隆方法,需要实现Cloneable接口。Cloneable接口是一个标识接口,仅做标记之用,接口中未声明任何方法。
浅拷贝和深拷贝就是克隆相关的两种方法。默认clone()方法实现的是浅拷贝。如果要实现深拷贝,需要覆盖clone()方法。下面开始详细介绍浅拷贝和深拷贝
Java中的浅拷贝:
Java中默认的clone()方法就是浅拷贝,即拷贝原有对象的值。如果原有对象有任何引用对象,则引用对象的属性值会拷贝过去,但不会创建新的对象(译者注:也就是说只是把内存地址拷贝过去,并未创建新的实例)。这就意味着任何对这些被克隆对象的修改都会导致克隆对象发生相同的变动,反之亦然。浅拷贝不是百分百与原有对象保持独立的关系,会互相影响。
class Course { String subject1; String subject2; String subject3; public Course(String sub1, String sub2, String sub3) { this.subject1 = sub1; this.subject2 = sub2; this.subject3 = sub3; } } class Student implements Cloneable { int id; String name; Course course; public Student(int id, String name, Course course) { this.id = id; this.name = name; this.course = course; } //Default version of clone() method. It creates shallow copy of an object. protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public class ShallowCopyInJava { public static void main(String[] args) { Course science = new Course("Physics", "Chemistry", "Biology"); Student student1 = new Student(111, "John", science); Student student2 = null; try { //Creating a clone of student1 and assigning it to student2 student2 = (Student) student1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } //Printing the subject3 of 'student1' System.out.println(student1.course.subject3); //Output : Biology //Changing the subject3 of 'student2' student2.course.subject3 = "Maths"; //This change will be reflected in original student 'student1' System.out.println(student1.course.subject3); //Output : Maths } }
在上面的例子中,student1是Student类的一个对象实例,有三个字段,分别是id,name,course。course是个指向Course的引用对象,student2是通过克隆student1创建出来。通过浅拷贝,course字段的默认值在student1和student2是一样的,都指向Course对象。因此,任何对对象student2的改动都会反映到student1中,反之亦然。
Java中的深拷贝:
深拷贝很像浅拷贝,也会拷贝实例对象的值,但是不同的是,会额外再创建引用对象的实例。这也就意味着克隆对象与原型对象是互相分离的,独立互不影响的。任何对克隆对象的更改不会对原型对象产生任何影响。
如果要创建深拷贝,需要覆盖clone()方法,如下代码所示:
class Course implements Cloneable { String subject1; String subject2; String subject3; public Course(String sub1, String sub2, String sub3) { this.subject1 = sub1; this.subject2 = sub2; this.subject3 = sub3; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Student implements Cloneable { int id; String name; Course course; public Student(int id, String name, Course course) { this.id = id; this.name = name; this.course = course; } //Overriding clone() method to create a deep copy of an object. protected Object clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); student.course = (Course) course.clone(); return student; } } public class DeepCopyInJava { public static void main(String[] args) { Course science = new Course("Physics", "Chemistry", "Biology"); Student student1 = new Student(111, "John", science); Student student2 = null; try { //Creating a clone of student1 and assigning it to student2 student2 = (Student) student1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } //Printing the subject3 of 'student1' System.out.println(student1.course.subject3); //Output : Biology //Changing the subject3 of 'student2' student2.course.subject3 = "Maths"; //This change will not be reflected in original student 'student1' System.out.println(student1.course.subject3); //Output : Biology } }
下图展示了student1如何被创建
Java中浅拷贝和深拷贝可用如下表格作对比:
浅拷贝 |
深拷贝 |
拷贝对象与原型对象不是绝对分离的 |
拷贝对象与原型对象绝对分离 |
对拷贝对象的更改会影响到原型对象 |
对拷贝对象的更改不会影响到原型对象 |
默认的clone()方法只是浅拷贝 |
要创建深拷贝需要覆盖clone()方法 |
浅拷贝只推荐实例对象是基本数据类型的情况 |
如果是除基本类型以外的对象,推荐深拷贝 |
浅拷贝速度快,开销低 |
深拷贝速度慢,开销高 |
这里推荐两篇文章,也是对浅拷贝和深拷贝讲述的比较好,可以看看,会有帮助
漫谈deepcopy(深拷贝)和shallowcopy(浅拷贝)
What is the difference between a deep copy and a shallow copy