1、拷贝是什么:
拷贝就是将一个对象的属性拷贝到另一个具有相同类型的对象中去。
2、拷贝分类
java拷贝分为浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)
2.1 浅拷贝
2.1.1 定义
拷贝得到的对象所有的变量(属性值)与原来的对象相同。如果是基本类型,拷贝的就是基本类型的值;如果是引用类型,拷贝的就是引用地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
2.1.2 图例
2.2.3 实现方式
2.2.4 例子
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable{
private String name;
private Integer age;
private Pet pet;
@Override
protected Object clone() {
try {
return super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet {
private String name;
private Integer age;
}
测试
public static void main(String[] args) {
Pet pet = new Pet("泰迪",1);
Person person = new Person("jack",18,pet);
Person clone = (Person)person.clone();
System.out.println("原始对象:" + person); // 原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
System.out.println("拷贝对象:" + clone); // 拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
System.out.println("原始对象 == 拷贝对象:" + (person == clone)); // 原始对象 == 拷贝对象:false
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName())); // 原始对象.字符串 == 拷贝对象.字符串:true
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge())); // 原始对象.Integer == 拷贝对象.Integer:true
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet())); // 原始对象.引用对象 == 拷贝对象.引用对象:true
// 改变拷贝对象中宠物的年龄
clone.getPet().setAge(2);
System.out.println("原始对象:" + person); // 原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=2))
System.out.println("拷贝对象:" + clone); // 拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=2))
System.out.println("原始对象 == 拷贝对象:" + (person == clone)); // 原始对象 == 拷贝对象:false
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName())); // 原始对象.字符串 == 拷贝对象.字符串:true
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge())); // 原始对象.Integer == 拷贝对象.Integer:true
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet())); //原始对象.引用对象 == 拷贝对象.引用对象:true
}
2.2 深拷贝
2.2.1 定义
拷贝对象的所有属性,并拷贝属性指向的动态分配的内存。与浅拷贝相比,深拷贝速度慢,开销大
2.2.2 代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable{
private String name;
private Integer age;
private Pet pet;
@Override
protected Object clone() {
try {
Person person = (Person) super.clone();
person.pet.clone();
return person;
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet implements Cloneable{
private String name;
private Integer age;
@Override
protected Object clone() {
try {
return super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
测试
public static void main(String[] args) {
Pet pet = new Pet("泰迪",1);
Person person = new Person("jack",18,pet);
Person clone = (Person)person.clone();
System.out.println("原始对象:" + person); // 原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
System.out.println("拷贝对象:" + clone); // 拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
System.out.println("原始对象 == 拷贝对象:" + (person == clone)); // 原始对象 == 拷贝对象:false
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName())); // 原始对象.字符串 == 拷贝对象.字符串:true
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge())); // 原始对象.Integer == 拷贝对象.Integer:true
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet())); // 原始对象.引用对象 == 拷贝对象.引用对象:true
// 改变拷贝对象中宠物的年龄
clone.getPet().setAge(2);
person.setAge(19);
System.out.println("原始对象:" + person); // 原始对象:Person(name=jack, age=19, pet=Pet(name=泰迪, age=2))
System.out.println("拷贝对象:" + clone); // 拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=2))
System.out.println("原始对象 == 拷贝对象:" + (person == clone)); // 原始对象 == 拷贝对象:false
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName())); // 原始对象.字符串 == 拷贝对象.字符串:true
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge())); // 原始对象.Integer == 拷贝对象.Integer:false
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet())); // 原始对象.引用对象 == 拷贝对象.引用对象:true
}
2.3、序列化拷贝
2.3.1 定义
通过实现序列号实现拷贝,属于深拷贝
序列化拷贝时,必须确保对象中所有类都是可序列化的
2.3.2 示例
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private String name;
private Integer age;
private Pet pet;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet implements Serializable {
private String name;
private Integer age;
}
测试
public static void main(String[] args) {
try {
Pet pet = new Pet("泰迪",1);
Person person = new Person("jack",18,pet);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(person);
oos.flush();
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
Person clone = (Person)ois.readObject();
System.out.println("原始对象:" + person);
System.out.println("拷贝对象:" + clone);
System.out.println("原始对象 == 拷贝对象:" + (person == clone));
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName()));
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge()));
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet()));
clone.getPet().setAge(2);
person.setAge(19);
System.out.println("原始对象:" + person);
System.out.println("拷贝对象:" + clone);
System.out.println("原始对象 == 拷贝对象:" + (person == clone));
System.out.println("原始对象.字符串 == 拷贝对象.字符串:" + (person.getName() == clone.getName()));
System.out.println("原始对象.Integer == 拷贝对象.Integer:" + (person.getAge() == clone.getAge()));
System.out.println("原始对象.引用对象 == 拷贝对象.引用对象:" + (person.getPet() == clone.getPet()));
}catch ( Exception e){
e.printStackTrace();
}
}
结果
原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
原始对象 == 拷贝对象:false
原始对象.字符串 == 拷贝对象.字符串:false
原始对象.Integer == 拷贝对象.Integer:false
原始对象.引用对象 == 拷贝对象.引用对象:false
=============================================================
// 修改拷贝对象中引用对象的属性值
// 修改原始对象的属性值
=============================================================
原始对象:Person(name=jack, age=19, pet=Pet(name=泰迪, age=1))
拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=2))
原始对象 == 拷贝对象:false
原始对象.字符串 == 拷贝对象.字符串:false
原始对象.Integer == 拷贝对象.Integer:false
原始对象.引用对象 == 拷贝对象.引用对象:false
结论
2.4 集合拷贝
通过构造函数,或者clone方法
2.4.1 集合浅拷贝
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable{
private String name;
private Integer age;
private Pet pet;
@Override
protected Object clone() {
try {
Person person = (Person) super.clone();
person.setPet((Pet)this.pet.clone());
return person;
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet implements Cloneable{
private String name;
private Integer age;
@Override
protected Object clone() {
try {
return super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
测试
public static void main(String[] args) throws Exception {
Pet pet = new Pet("泰迪",1);
Person person = new Person("jack",18,pet);
ArrayList<Person> list = new ArrayList<>();
list.add(person);
ArrayList<Person> cloneList= (ArrayList<Person>) list.clone();
System.out.println("原始对象地址:" + System.identityHashCode(list.get(0)));
System.out.println("拷贝对象地址:" + System.identityHashCode(cloneList.get(0)));
System.out.println("原始对象:" + list.get(0));
System.out.println("拷贝对象:" + cloneList.get(0));
System.out.println("原始对象 == 拷贝对象:" + (list.get(0) == cloneList.get(0)));
System.out.println("原始对象.equals(拷贝对象):" + (list.get(0).equals(cloneList.get(0))));
cloneList.get(0).setAge(20);
list.get(0).getPet().setAge(2);
System.out.println("原始对象地址:" + System.identityHashCode(list.get(0)));
System.out.println("拷贝对象地址:" + System.identityHashCode(cloneList.get(0)));
System.out.println("原始对象:" + list.get(0));
System.out.println("拷贝对象:" + cloneList.get(0));
System.out.println("原始对象 == 拷贝对象:" + (list.get(0) == cloneList.get(0)));
System.out.println("原始对象.equals(拷贝对象):" + (list.get(0).equals(cloneList.get(0))));
}
结果
原始对象地址:925858445
拷贝对象地址:925858445
原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
原始对象 == 拷贝对象:true
原始对象.equals(拷贝对象):true
==========================================================
// 修改原始对象
// 修改拷贝对象
===========================================================
原始对象地址:925858445
拷贝对象地址:925858445
原始对象:Person(name=jack, age=20, pet=Pet(name=泰迪, age=2))
拷贝对象:Person(name=jack, age=20, pet=Pet(name=泰迪, age=2))
原始对象 == 拷贝对象:true
原始对象.equals(拷贝对象):true
2.4.2 集合深拷贝
新建一个集合,并使用对象的深拷贝
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable{
private String name;
private Integer age;
private Pet pet;
@Override
protected Object clone() {
try {
Person person = (Person) super.clone();
person.setPet((Pet)this.pet.clone());
return person;
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet implements Cloneable{
private String name;
private Integer age;
@Override
protected Object clone() {
try {
return super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return null;
}
}
测试
public static void main(String[] args) throws Exception {
Pet pet = new Pet("泰迪",1);
Person person = new Person("jack",18,pet);
ArrayList<Person> list = new ArrayList<>();
list.add(person);
ArrayList<Person> cloneList = new ArrayList<>();
for (Person p : list) {
cloneList.add((Person)p.clone());
}
System.out.println("原始对象地址:" + System.identityHashCode(list.get(0)));
System.out.println("拷贝对象地址:" + System.identityHashCode(cloneList.get(0)));
System.out.println("原始对象:" + list.get(0));
System.out.println("拷贝对象:" + cloneList.get(0));
System.out.println("原始对象 == 拷贝对象:" + (list.get(0) == cloneList.get(0)));
System.out.println("原始对象.equals(拷贝对象):" + (list.get(0).equals(cloneList.get(0))));
cloneList.get(0).setAge(20);
list.get(0).getPet().setAge(2);
System.out.println("原始对象地址:" + System.identityHashCode(list.get(0)));
System.out.println("拷贝对象地址:" + System.identityHashCode(cloneList.get(0)));
System.out.println("原始对象:" + list.get(0));
System.out.println("拷贝对象:" + cloneList.get(0));
System.out.println("原始对象 == 拷贝对象:" + (list.get(0) == cloneList.get(0)));
System.out.println("原始对象.equals(拷贝对象):" + (list.get(0).equals(cloneList.get(0))));
}
结果
原始对象地址:925858445
拷贝对象地址:798154996
原始对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=1))
原始对象 == 拷贝对象:false
原始对象.equals(拷贝对象):true
============================================================
============================================================
原始对象地址:925858445
拷贝对象地址:798154996
原始对象:Person(name=jack, age=20, pet=Pet(name=泰迪, age=1))
拷贝对象:Person(name=jack, age=18, pet=Pet(name=泰迪, age=2))
原始对象 == 拷贝对象:false
原始对象.equals(拷贝对象):false
总结
浅拷贝、深拷贝 原始对象与拷贝对象不是一个对象,对于基本类型,改变一个对象的属性值不影响另一个其他对象,对于引用类型,由于浅拷贝只是拷贝引用类型的地址,改变引用对象的属性值,另一个对象中的引用对象的属性值也发生改变。而深拷贝时,引用类型也进行了拷贝,产生一个新的引用对象,所以改变引用对象的属性值,另一个象中的引用对象的属性值不发生改变