Bootstrap

JAVA浅克隆和深克隆


前言

克隆的使用场景主要为需要将对象将源对象复制一份出来,保证后续改动不会对源对象造成影响。
浅克隆复制基本类型变量,改动克隆对象引用对象属性,会对源对象造成影响。
深克隆复制基本类型变量和引用类型变量,和源对象完全分割,改动克隆对象不会影响源对象。


一、浅克隆

1. 实体对象

package com.student.model;

/**
 * Create by zjg on 2024/7/30
 */
public class Student {
    private int id;
    private String name;
    private int age;
    private String gender;
    private Address address;
    public Student() {
    }
    public Student(int id, String name, int age, String gender, Address address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", address=" + address +
                '}';
    }

    public static class Address implements Cloneable{
        private String province;
        private String city;
        private String area;
        public Address(String province, String city, String area) {
            this.province = province;
            this.city = city;
            this.area = area;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getArea() {
            return area;
        }

        public void setArea(String area) {
            this.area = area;
        }

        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    ", area='" + area + '\'' +
                    '}';
        }
    }
}

2. 单元测试

package test.z202401;

import com.student.model.Student;
import org.springframework.beans.BeanUtils;

/**
 * Create by zjg on 2024/7/30
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student=new Student(1,"张三",18,"男",new Student.Address("北京","北京","朝阳区"));
        Student clone = new Student();
        BeanUtils.copyProperties(student,clone);
        System.out.println("修改前------");
        System.out.println(student);
        System.out.println(clone);
        clone.setId(2);//修改克隆对象的id
        clone.setName("李四");//修改克隆对象的名字
        Student.Address address = clone.getAddress();
        address.setProvince("山东省");//修改克隆对象的省
        address.setCity("济南市");//修改克隆对象的市
        address.setArea("市中区");//修改克隆对象的区
        clone.setAddress(address);
        System.out.println("修改后------");
        System.out.println(student);
        System.out.println(clone);
    }
}

3. 输出

修改前------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
修改后------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='山东省', city='济南市', area='市中区'}}
Student{id=2, name='李四', age=18, gender='男', address=Address{province='山东省', city='济南市', area='市中区'}}

我们修改了克隆对象的id,nameaddress对象,对象1的idname没有发生变化,但是对于我们自定义的Address对象修改却影响了对象1的数据,可见它们使用的是同一个Address对象。

二、深克隆

深度克隆有Cloneable和Serializable两种实现方式,都能解决上述问题,接下来我们逐个介绍

1. Cloneable

1.1 实体对象

package com.student.model;

/**
 * Create by zjg on 2024/7/30
 */
public class Student implements Cloneable{
    private int id;
    private String name;
    private int age;
    private String gender;
    private Address address;
    public Student() {
    }
    public Student(int id, String name, int age, String gender, Address address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
    public Object clone() throws CloneNotSupportedException {
        Student student = (Student)super.clone();
        student.address = (Address)this.address.clone();
        return student;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", address=" + address +
                '}';
    }

    public static class Address implements Cloneable{
        private String province;
        private String city;
        private String area;
        public Address(String province, String city, String area) {
            this.province = province;
            this.city = city;
            this.area = area;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getArea() {
            return area;
        }

        public void setArea(String area) {
            this.area = area;
        }
        public Object clone() throws CloneNotSupportedException {
            Address address = (Address)super.clone();
            return address;
        }
        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    ", area='" + area + '\'' +
                    '}';
        }
    }
}

1.2 单元测试

package test.z202401;

import com.student.model.Student;

/**
 * Create by zjg on 2024/7/30
 */
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student=new Student(1,"张三",18,"男",new Student.Address("北京","北京","朝阳区"));
        Student clone = (Student)student.clone();
        System.out.println("修改前------");
        System.out.println(student);
        System.out.println(clone);
        clone.setId(2);//修改克隆对象的id
        clone.setName("李四");//修改克隆对象的名字
        Student.Address address = clone.getAddress();
        address.setProvince("山东省");//修改克隆对象的省
        address.setCity("济南市");//修改克隆对象的市
        address.setArea("市中区");//修改克隆对象的区
        clone.setAddress(address);
        System.out.println("修改后------");
        System.out.println(student);
        System.out.println(clone);
    }
}

1.3 输出

修改前------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
修改后------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
Student{id=2, name='李四', age=18, gender='男', address=Address{province='山东省', city='济南市', area='市中区'}}

可以看出修改克隆对象的属性,不会对原对象造成影响

2. Serializable

2.1 实体对象

package com.student.model;

import java.io.Serializable;

/**
 * Create by zjg on 2024/7/30
 */
public class Student implements Serializable {
    private int id;
    private String name;
    private int age;
    private String gender;
    private Address address;
    public Student() {
    }
    public Student(int id, String name, int age, String gender, Address address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", address=" + address +
                '}';
    }

    public static class Address implements Serializable {
        private String province;
        private String city;
        private String area;
        public Address(String province, String city, String area) {
            this.province = province;
            this.city = city;
            this.area = area;
        }

        public String getProvince() {
            return province;
        }

        public void setProvince(String province) {
            this.province = province;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getArea() {
            return area;
        }

        public void setArea(String area) {
            this.area = area;
        }

        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    ", area='" + area + '\'' +
                    '}';
        }
    }
}

2.2 单元测试

package test.z202401;

import com.student.model.Student;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * Create by zjg on 2024/7/30
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student=new Student(1,"张三",18,"男",new Student.Address("北京","北京","朝阳区"));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(student);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Student clone = (Student)ois.readObject();
        System.out.println("修改前------");
        System.out.println(student);
        System.out.println(clone);
        clone.setId(2);//修改克隆对象的id
        clone.setName("李四");//修改克隆对象的名字
        Student.Address address = clone.getAddress();
        address.setProvince("山东省");//修改克隆对象的省
        address.setCity("济南市");//修改克隆对象的市
        address.setArea("市中区");//修改克隆对象的区
        clone.setAddress(address);
        System.out.println("修改后------");
        System.out.println(student);
        System.out.println(clone);
    }
}

2.3 输出

修改前------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
修改后------
Student{id=1, name='张三', age=18, gender='男', address=Address{province='北京', city='北京', area='朝阳区'}}
Student{id=2, name='李四', age=18, gender='男', address=Address{province='山东省', city='济南市', area='市中区'}}

实现序列化接口,通过对象流产生的对象也是不会对原对象产生影响。


总结

回到顶部

深度克隆的两种使用方式略有区别,大家可以根据自己的使用习惯来

实践是检验真理的唯一标准。

;