对象拷贝
Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去。
假设有一个类S,它有两个对象A和B,分别具有属性a和b,则对对象A进行拷贝操作赋值给对象B就是:
- B.a = A.a;
- B.b = A.b;
浅拷贝
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
简单来说,浅拷贝就是对基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝。
通过拷贝构造方法实现浅拷贝
定义类Person,将Person类对象p1拷贝至对象p2。代码如下:
public class Test {
public static void main(String[] args) {
Person p1 = new Person("阿伟",20);
Person p2 = new Person(p1);
p1.setName("杰哥");
System.out.println("p1:" + p1);
System.out.println("p2:" + p2);
}
}
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person(Person p) {
this.name = p.name;
this.age = p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果如下:
通过重写clone方法实现浅拷贝
通过clone方法实现浅拷贝,要使类实现Cloneable接口并在类中重写clone方法,代码如下:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("阿伟",20);
Person p2 = p1.clone();
p1.setName("杰哥");
System.out.println("p1:" + p1);
System.out.println("p2:" + p2);
}
}
class Person implements Cloneable{
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person(Person p) {
this.name = p.name;
this.age = p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
运行结果如下:
深拷贝
对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。故深拷贝相比于浅拷贝速度较慢并且花销较大。
简单来说,深拷贝对基本数据类型进行值传递,对引用数据类型创建一个新的对象,并复制其内容。深拷贝对嵌套属性的对象也进行拷贝
通过重写clone方法实现深拷贝
通过clone方法实现深拷贝,要使类实现Cloneable接口并在类中重写clone方法,并且在clone方法内部,把该对象引用的其他对象也要clone一份,这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。代码如下:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Age age = new Age(20);
Person p1 = new Person("阿伟",age);
Person p2 = (Person)p1.clone();
System.out.println("p1:" + p1);
System.out.println("p2:" + p2);
p1.setName("杰哥");
System.out.println("修改后p1:" + p1);
System.out.println("修改后p2:" + p2);
}
}
class Age implements Cloneable {
private int age;
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return age + "";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = null;
obj = super.clone();
return obj;
}
}
class Person implements Cloneable {
private String name;
private Age age;
public Person(String name,Age age) {
this.name = name;
this.age = age;
}
public Person(Person p) {
this.name = p.name;
this.age = p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = null;
obj = super.clone();
Person p = (Person) obj;
p.age = (Age) p.getAge().clone();
return obj;
}
}
运行结果如下:
总结
- 在浅拷贝中,基本数据类型是值传递,所以修改值后不会影响另一个对象的该属性值;而引用数据类型是地址传递(引用传递),所以修改值后另一个对象的该属性值会同步被修改。
- 在深拷贝中,因为为引用类型的数据成员另辟了一个独立的内存空间,故无论是什么类型的属性值的修改,都不会影响另一个对象的属性值。
- 深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间;而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。
参考文章
https://www.cnblogs.com/shakinghead/p/7651502.html
https://baijiahao.baidu.com/s?id=1671117492706772194&wfr=spider&for=pc
https://www.jianshu.com/p/94dbef2de298