转载自:https://www.jianshu.com/p/94dbef2de298
1. 浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
浅拷贝的特点:
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。
浅拷贝的实现:
实现对象拷贝的类,需要实现 Cloneable
接口,并覆写 clone()
方法。
1 public class Subject { 2 private String name; 3 public Subject(String name) { 4 this.name = name; 5 } 6 public String getName() { 7 return name; 8 } 9 public void setName(String name) { 10 this.name = name; 11 } 12 @Override 13 public String toString() { 14 return "[Subject: " + this.hashCode() + ",name:" + name + "]"; 15 } 16 } 17 public class Student implements Cloneable { 18 //引用类型 19 private Subject subject; 20 //基础数据类型 21 private String name; 22 private int age; 23 //getters and setters 24 //... 25 /** 26 * 重写clone()方法 27 * @return 28 */ 29 @Override 30 public Object clone() { 31 //浅拷贝 32 try { 33 // 直接调用父类的clone()方法 34 return super.clone(); 35 } catch (CloneNotSupportedException e) { 36 return null; 37 } 38 } 39 @Override 40 public String toString() { 41 return "[Student: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]"; 42 } 43 } 44 45 public class ShallowCopy { 46 public static void main(String[] args) { 47 Subject subject = new Subject("yuwen"); 48 Student studentA = new Student(); 49 50 studentA.setSubject(subject); 51 studentA.setName("Lynn"); 52 studentA.setAge(20); 53 54 Student studentB = (Student) studentA.clone(); //浅拷贝 55 studentB.setName("Lily"); 56 studentB.setAge(18); 57 Subject subjectB = studentB.getSubject(); 58 subjectB.setName("lishi"); 59 System.out.println("studentA:" + studentA.toString()); 60 System.out.println("studentB:" + studentB.toString()); 61 } 62 } 63 64 //输出结果 65 studentA:[Student: 460141958,subject:[Subject: 1163157884,name:lishi],name:Lynn,age:20] 66 studentB:[Student: 1956725890,subject:[Subject: 1163157884,name:lishi],name:Lily,age:18]
通过浅拷贝,studentA和studentB的对象地址不一样(说明浅拷贝生成了一个新的对象),基本数据类型都不一样。但是引用数据类型是一样的,而且引用的对象Subject是同一个对象,说明浅拷贝中只是复制了对象引用的地址。
2. 对象拷贝
public static void main(String[] args) { Subject subject = new Subject("yuwen"); Student studentA = new Student(); studentA.setSubject(subject); studentA.setName("Lynn"); studentA.setAge(20); Student studentB = studentA; //对象拷贝 studentB.setName("Lily"); studentB.setAge(18); Subject subjectB = studentB.getSubject(); subjectB.setName("lishi"); System.out.println("studentA:" + studentA.toString()); System.out.println("studentB:" + studentB.toString()); }
运行结果:
studentA:[Student: 460141958,subject:[Subject: 1163157884,name:lishi],name:Lily,age:18]
studentB:[Student: 460141958,subject:[Subject: 1163157884,name:lishi],name:Lily,age:18]
说明对象拷贝没有生成新的对象,两者的地址是一样的。而浅拷贝生成了一个不一样的对象。
3. 深拷贝
深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
深拷贝的特点:
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable
并重写 clone()
方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。
深拷贝的实现:
对于 Student
的引用类型的成员变量 Subject
,需要实现 Cloneable
并重写 clone()
方法。
public class Subject implements Cloneable { private String name; public Subject(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { //Subject 如果也有引用类型的成员属性,也应该和 Student 类一样实现 return super.clone(); } @Override public String toString() { return "[Subject: " + this.hashCode() + ",name:" + name + "]"; } }
在 Student
的 clone()
方法中,需要拿到拷贝自己后产生的新的对象,然后对新的对象的引用类型再调用拷贝操作,实现对引用类型成员变量的深拷贝。
public class Student implements Cloneable { //引用类型 private Subject subject; //基础数据类型 private String name; private int age; //getters and setters //... /** * 重写clone()方法 * @return */ @Override public Object clone() { //深拷贝 try { // 直接调用父类的clone()方法 Student student = (Student) super.clone(); student.subject = (Subject) subject.clone(); return student; } catch (CloneNotSupportedException e) { return null; } } @Override public String toString() { return "[Student: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]"; } } public class ShallowCopy { public static void main(String[] args) { Subject subject = new Subject("yuwen"); Student studentA = new Student(); studentA.setSubject(subject); studentA.setName("Lynn"); studentA.setAge(20); Student studentB = (Student) studentA.clone(); studentB.setName("Lily"); studentB.setAge(18); Subject subjectB = studentB.getSubject(); subjectB.setName("lishi"); System.out.println("studentA:" + studentA.toString()); System.out.println("studentB:" + studentB.toString()); } }
由输出结果可见,深拷贝后,不管是基础数据类型还是引用类型的成员变量,修改其值都不会相互造成影响。