- 拷贝
拷贝,顾名思义就是为了获得一个相同的对象,而不需要我们再人为的创建和赋值。Java中的对象拷贝(Object Copy)指的就是将一个对象的所有属性复制给另一个有着相同类类型的对象。
有两个对象,分别为对象A和对象B,同时这两个对象都属于XX类,并且对象A和对象B都具有属性a和b,那么将对象A拷贝给对象B的操作过程就是:B.a = A.a; B.b = A.b;
使用拷贝的目的主要是为了在新的上下文环境中复用现有对象的部分(或全部)数据。
- 浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝。
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将这个属性值复制一份给新的对象,因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据;对于数据类型是引用数据类型的成员变量,比如说该成员变量是某个数组或某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(即内存地址)复制一份给新的对象,也就说这两个不同对象的该成员变量都指向了同一个地址,此时在一个对象中修改该成员变量就会影响到另一个对象所拷贝得到的数据。
实现浅拷贝的方法也很简单,只需要将需要拷贝的类实现 Cloneable 接口并覆写 clone() 方法即可:
public class Subject {
private String name;
public Subject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Subject{" +
"name='" + name + '\'' +
'}';
}
}
public class Person implements Cloneable {
/**
* 引用数据类型
*/
private Subject subject;
/**
* 基础数据类型
*/
private String name;
/**
* 基础数据类型
*/
private int age;
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
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;
}
/**
* 重写clone()方法
* @return
*/
@Override
public Object clone() {
//浅拷贝
try {
// 直接调用父类的clone()方法
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public String toString() {
return "[Person: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]";
}
}
public class Test {
public static void main(String[] args) {
Subject subject = new Subject("张三");
Person personA = new Person();
personA.setSubject(subject);
personA.setName("李四");
personA.setAge(20);
Person personB = (Person) personA.clone();
personB.setName("王五");
personB.setAge(18);
Subject subjectB = personB.getSubject();
subjectB.setName("赵六");
System.out.println("PersonA:" + personA.toString());
System.out.println("PersonB:" + personB.toString());
}
}
我们通过运行结果就能看出,PersonB 是通过 personA.clone() 拷贝后得到的 ,但是 personA 和 personB 是两个不同的对象。personA 和 personB 的基础数据类型的修改互不影响,而引用类型 subject 的值被修改后,另一个对象的 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 {
return super.clone();
}
@Override
public String toString() {
return "Subject{" +
"name='" + name + '\'' +
'}';
}
}
public class Person implements Cloneable {
/**
* 引用数据类型
*/
private Subject subject;
/**
* 基础数据类型
*/
private String name;
/**
* 基础数据类型
*/
private int age;
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
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;
}
/**
* 重写clone()方法
* 如果Subject类中也存在引用对象,则需要和Person类一样去实现深拷贝
* @return
*/
@Override
public Object clone() {
//深拷贝
try {
Person person = (Person) super.clone();
person.subject = (Subject) subject.clone();
return person;
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
return null;
}
}
@Override
public String toString() {
return "[Person: " + this.hashCode() + ",subject:" + subject + ",name:" + name + ",age:" + age + "]";
}
}
public class Test {
public static void main(String[] args) {
Subject subject = new Subject("张三");
Person personA = new Person();
personA.setSubject(subject);
personA.setName("李四");
personA.setAge(20);
Person personB = (Person) personA.clone();
personB.setName("王五");
personB.setAge(18);
Subject subjectB = personB.getSubject();
subjectB.setName("赵六");
System.out.println("PersonA:" + personA.toString());
System.out.println("PersonB:" + personB.toString());
}
}
通过运行结果我们可以看出,深拷贝对象后,修改其中某个对象的基础数据类型变量或引用类型的成员变量后都不会对另一个对象造成影响。