• Java中的深浅拷贝方式


    Java中的深浅拷贝方式

    实现Cloneable重写clone()方法

    如何进行对象克隆

    Object对象有个clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:

    ① 实现Cloneable接口,这是一个标记接口,自身没有方法。

    ② 覆盖clone()方法,可见性提升为public。

    浅拷贝和深拷贝

    浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象

    深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象

    也就是说,一个默认的clone()方法实现机制,仍然是赋值,如果一个呗赋值的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝,如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable接口并覆盖clone()方法

    浅拷贝案例

    @Data
    public class Person implements Cloneable {
        private String name;
        private Integer age;
    
        @Override
        public Person clone(){
            try {
                return (Person)super.clone();
            } catch (CloneNotSupportedException e) {
                return new Person();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试结果

        @Test
        public void test1() {
            Person p1 = new Person();
            p1.setAge(31);
            p1.setName("Peter");
    
            Person p2 = p1.clone();
            System.out.println(p1 == p2);//false
            p2.setName("Jacky");
            System.out.println("p1=" + p1);//p1=Person [name=Peter, age=31]
            System.out.println("p2=" + p2);//p2=Person(name=Jacky, age=31)
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    深拷贝案例

    修改person类,添加address属性

    @Data
    public class Address {
        private String type;
        private String value;
    }
    
    @Data
    public class Person implements Cloneable {
        private String name;
        private Integer age;
        private Address address;
    
        @Override
        public Person clone(){
            try {
                return (Person)super.clone();
            } catch (CloneNotSupportedException e) {
                return new Person();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试结果

        @Test
        public void testShallowCopy(){
            Address address=new Address();
            address.setType("Home");
            address.setValue("北京");
    
            Person p1=new Person();
            p1.setAge(31);
            p1.setName("Peter");
            p1.setAddress(address);
    
            Person p2=p1.clone();
            System.out.println(p1==p2);//false
    
            p2.getAddress().setType("Office");
            System.out.println("p1="+p1);//p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
            System.out.println("p2="+p2);//p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里我们可以发现只修改p2的address,可是结果显示p1和p2的address都进行了修改

    这里如果要保证只修改p2的address,需要address实体类也实现Cloneable接口和重写clone方法,并且Person的clone()需要显式地clone其引用成员

    @Data
    public class Address implements Cloneable{
        private String type;
        private String value;
    
        @Override
        public Address clone(){
            try {
                return (Address) super.clone();
            } catch (CloneNotSupportedException e) {
                return new Address();
            }
        }
    }
    
    @Data
    public class Person implements Cloneable {
        private String name;
        private Integer age;
        private Address address;
    
        @Override
        public Person clone(){
            try {
                Person person = (Person) super.clone();
                Address address = person.getAddress();
                person.setAddress(address.clone());
                return person;
            } catch (CloneNotSupportedException e) {
                return new Person();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    重新跑前面的测试用例:

    false
    p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
    p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
    
    • 1
    • 2
    • 3

    clone方式小结

    • 如果有一个非原生成员,如自定义对象的成员,那么就需要:
      • 该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。
      • 同时,修改被复制类的clone()方法,增加成员的克隆逻辑
    • 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法

    原型工厂类

    为了方便测试,节省篇幅,封装一个工程类

    public class PersonFactory{
        public static Person newPrototypeInstance(){
            Address address = new Address();
            address.setType("Home");
            address.setValue("北京");
     
            Person p1 = new Person();
            p1.setAddress(address);
            p1.setAge(31);
            p1.setName("Peter");
            return p1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    利用Dozer拷贝对象

    maven依赖

    <dependency>
      <groupId>net.sf.dozer</groupId>
      <artifactId>dozer</artifactId>
      <version>5.5.1</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试用例:

       @Test
        public void testDozer() {
            Person p1= PersonFactory.newPrototypeInstance();
            Mapper mapper = new DozerBeanMapper();
            Person p2 = mapper.map(p1, Person.class);
            p2.getAddress().setType("Office");
            System.out.println("p1=" + p1);
            System.out.println("p2=" + p2);
        }
    
    @Data
    public class Person{
        private String name;
        private Integer age;
        private Address address;
    }
    
    @Data
    public class Address {
        private String type;
        private String value;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出结果:

    p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
    p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
    
    • 1
    • 2

    利用Commons-BeanUtils复制对象

    maven依赖

    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.3</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试用例:

        @Test
        public void testCommonsBeanUtils(){
            Person p1=PersonFactory.newPrototypeInstance();
            try {
                Person p2=(Person) BeanUtils.cloneBean(p1);
                //p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
                System.out.println("p1=" + p1);
                p2.getAddress().setType("Office");
                //p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
                System.out.println("p2=" + p2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Orika复制对象

    maven依赖

    <dependency>
      <groupId>ma.glasnost.orika</groupId>
      <artifactId>orika-core</artifactId>
      <version>1.5.0</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试用例

    @Test
    public void testOrika() {
        MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    
        mapperFactory.classMap(Person.class, Person.class)
                .byDefault()
                .register();
        MapperFacade mapper = mapperFactory.getMapperFacade();
    
        Person p1=PersonFactory.newPrototypeInstance();
        Person p2 = mapper.map(p1, Person.class);
        System.out.println("p1=" + p1);
        p2.getAddress().setType("Office");
        System.out.println("p2=" + p2);
    }
    
    p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
    p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    【单片机】LED模块和独立按键的使用
    webrtc iOS--mediasoup
    《信息安全技术》学习笔记01
    详解Native Memory Tracking之追踪区域分析
    优炫软件中标西南民族大学项目,护航教育行业主机安全
    深入理解nginx的https sni机制
    第二十章 控制进程(一)
    javascript算法排序之桶排序
    Java EE 用户删除和修改功能
    盲注原理基础
  • 原文地址:https://blog.csdn.net/weixin_43296313/article/details/125441329