java拷贝

169 阅读8分钟

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

总结

浅拷贝、深拷贝 原始对象与拷贝对象不是一个对象,对于基本类型,改变一个对象的属性值不影响另一个其他对象,对于引用类型,由于浅拷贝只是拷贝引用类型的地址,改变引用对象的属性值,另一个对象中的引用对象的属性值也发生改变。而深拷贝时,引用类型也进行了拷贝,产生一个新的引用对象,所以改变引用对象的属性值,另一个象中的引用对象的属性值不发生改变