zoukankan      html  css  js  c++  java
  • 【java编程】java对象copy

    实现java对象Copy的三种方式

    一、克隆

    implements Cloneable

    二、序列化

    implements Serializable

    三、利用反射机制copy

    apache的BeanUtils方案

    使用org.apache.commons.beanutils.BeanUtils进行对象深入复制时候,主要通过向BeanUtils框架注入新的类型转换器,因为默认情况下,BeanUtils对复杂对象的复制是引用,

    可以发现,使用org.apache.commons.beanutils.BeanUtils复制引用时,主和源的引用为同一个,即改变了主的引用属性会影响到源的引用,所以这是一种浅拷贝

    apache的PropertyUtils方案

    PropertyUtils的copyProperties()方法几乎与BeanUtils.copyProperties()相同,主要的区别在于后者提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,PropertyUtils不支持这个功能,所以说BeanUtils使用更普遍一点,犯错的风险更低一点。而且它仍然属于浅拷贝。

    Apache提供了 SerializationUtils.clone(T),T对象需要实现 Serializable 接口,他属于深克隆。

    spring的BeanUtils方案

    Spring中的BeanUtils,其中实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。

    可以看到, 成员变量赋值是基于目标对象的成员列表, 并且会跳过ignore的以及在源对象中不存在的, 所以这个方法是安全的, 不会因为两个对象之间的结构差异导致错误, 但是必须保证同名的两个成员变量类型相同.

    dozer

    Dozer(http://dozer.sourceforge.net/)能够实现深拷贝。Dozer是基于反射来实现对象拷贝,反射调用set/get 或者是直接对成员变量赋值 。 该方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。

    MapStrcut

    MapStrcut属于编译期的对象复制方案,它能够动态生成set/get代码的class文件 ,在运行时直接调用该class文件。该方式实际上扔会存在set/get代码,只是不需要自己写了。

    BeanCopier

    可以通过缓存BeanCopier的实例来提高性能。

    fastjson和GSON

    使用fastjson和GSON主要是通过对象json序列化和反序列化来完成对象复制,这里只是提供一种不一样的对象拷贝的思路,例子略。

    Spring的BeanUtils比较稳定,不会因为量大了,耗时明显增加,但其实基准耗时比较长;apache的BeanUtils稳定性与效率都不行,不可取;Gson,因为做两个gson转换,所以正常项目中,可能耗时会更少一些;PojoUtils稳定不如spring,但是总耗时优势明显,原因是它只是根据项目的需求,实现的简单的转换模板,这个代码在其它的几个工具类均有。

    而在网上的其他Blog中(参见Reference),对Apache的BeanUtils、PropertyUtils和CGLIB的BeanCopier作了性能测试。

    测试结果:

    性能对比: BeanCopier > BeanUtils. 其中BeanCopier的性能高出另外两个100数量级。

    综上推荐使用:

    1. BeanUtils(简单,易用)
    2. BeanCopier(加入缓存后和手工set的性能接近)
    3. Dozer(深拷贝)
    4. fastjson(特定场景下使用)

    一、克隆

    Java中创建对象除了调用构造函数来创建以外,还有几种利用语言之外的机制来创建的方式,clone就是其中的一种(还有一种就是前面说过的序列化的方式)。

    比如我们有一个Person类,有两个属性,一个年龄age,一个姓名name(又是熟悉的Person~)

    (1)浅拷贝

    实现浅拷贝的步骤是:

    1. 被复制的类需要实现Cloneable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常)改接口为标记接口(不含任何方法)
    2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)

    javaBean:

    public class Person implements Cloneable{
        private int age;
        private String name;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        protected Person clone() throws CloneNotSupportedException {
            return (Person) super.clone();
        }
    }
    View Code

    测试类:

    public class CloneTest {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Person person1=new Person();
            person1.setAge(10);
            person1.setName("sxf");
    
            Person person2=person1.clone();
            person2.setName("chn");
            //运行结果:
            //person1 name:sxf
            //person2 name:chn
            System.out.println("person1 name:"+person1.getName());
            System.out.println("person2 name:"+person2.getName());
    
        }
    }
    View Code

    如果类中含有引用类型属性,则浅copy失效。

    其实原理很简单,clone的对象根据原始对象从堆中开辟一块同等大小的内存,然后把原始对象的数据都复制到新的内存地址,对于基本类型,可以把原始值复制过来,但是对于引用类型,其保存的只是一个地址,复制时也是对地址的复制,最终还是指向同一个对象,所以也就造成了下面的问题。

    给javaBean新增一个引用属性的Bean的信息

    public class Person implements Cloneable{
        private int age;
        private String name;
        private Pet pet;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Pet getPet() {
            return pet;
        }
    
        public void setPet(Pet pet) {
            this.pet = pet;
        }
    
        @Override
        protected Person clone() throws CloneNotSupportedException {
            return (Person) super.clone();
        }
    }
    
    
    
    public class Pet {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    View Code

    测试类:

    public class CloneTest {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Person person1=new Person();
            person1.setAge(10);
            person1.setName("sxf");
            Pet pet1=new Pet();
            pet1.setName("xx");
            person1.setPet(pet1);
    
            Person person2=person1.clone();
            person2.setName("chn");
            person2.getPet().setName("yy");
            //运行结果:
            //person1 name:sxf
            //person2 name:chn
            //person1 pet name:yy
            //person2 pet name:yy
            System.out.println("person1 name:"+person1.getName());
            System.out.println("person2 name:"+person2.getName());
            System.out.println("person1 pet name:"+person1.getPet().getName());
            System.out.println("person2 pet name:"+person2.getPet().getName());
    
        }
    }
    View Code

    (2)深拷贝

    深拷贝就是把一个对象中的所有对象都拷贝一遍。

    就上述例子进行改良的bean

    public class Person implements Cloneable {
        private int age;
        private String name;
        private Pet pet;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Pet getPet() {
            return pet;
        }
    
        public void setPet(Pet pet) {
            this.pet = pet;
        }
    
        @Override
        protected Person clone() throws CloneNotSupportedException {
            Person p = (Person) super.clone();
            Pet pet = this.pet.clone();
            p.setPet(pet);
            return p;
        }
    }
    
    
    
    
    public class Pet implements Cloneable{
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        protected Pet clone() throws CloneNotSupportedException {
            return (Pet) super.clone();
        }
    }
    View Code

    测试类

    public class CloneTest {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Person person1=new Person();
            person1.setAge(10);
            person1.setName("sxf");
            Pet pet1=new Pet();
            pet1.setName("xx");
            person1.setPet(pet1);
    
            Person person2=person1.clone();
            person2.setName("chn");
            person2.getPet().setName("yy");
            //运行结果:
            //person1 name:sxf
            //person2 name:chn
            //person1 pet name:xx
            //person2 pet name:yy
            System.out.println("person1 name:"+person1.getName());
            System.out.println("person2 name:"+person2.getName());
            System.out.println("person1 pet name:"+person1.getPet().getName());
            System.out.println("person2 pet name:"+person2.getPet().getName());
    
        }
    }
    View Code

    但是这样写还会存在一个问题,如果一个对象中嵌套的对象比较多,我们还得去把每一个对象都实现Cloneable接口,如果对象里面嵌套对象,clone方法的处理也会比较复杂,有没有一种简单的方式去实现呢?

    还记得我们之前学习过的序列化方式吗?通过输入输出流来实现对象的深拷贝,这也是我们最开始提到的创建对象的另一种方式。

    二、序列化

    javaBean的改造

    package com.spring.test.service.javacore.clone;
    
    import java.io.Serializable;
    
    
    public class Person implements Serializable {
        private int age;
        private String name;
        private Pet pet;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Pet getPet() {
            return pet;
        }
    
        public void setPet(Pet pet) {
            this.pet = pet;
        }
    
    }
    
    
    public class Pet implements Serializable{
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    View Code

    测试类

    public class CloneTest {
    
        public static void main(String[] args) {
            Person person1=new Person();
            person1.setName("sxf");
            person1.setAge(10);
            Pet pet1=new Pet();
            pet1.setName("xx");
            person1.setPet(pet1);
            Person person2=SerializationUtils.clone(person1);
            person2.setName("chn");
            person2.getPet().setName("yy");
            //person1 name:sxf
            //person2 name:chn
            //pseron1 pet1 name:xx
            //pseron2 pet2 name:yy
            System.out.println("person1 name:"+person1.getName());
            System.out.println("person2 name:"+person2.getName());
            System.out.println("pseron1 pet1 name:"+person1.getPet().getName());
            System.out.println("pseron2 pet2 name:"+person2.getPet().getName());
    
        }
    }
    View Code
  • 相关阅读:
    oracle 导入数据时提示只有 DBA 才能导入由其他 DBA 导出的文件
    oracle 常用语句
    android udp 无法收到数据 (模拟器中)
    android DatagramSocket send 发送数据出错
    AtCoder ABC 128E Roadwork
    AtCoder ABC 128D equeue
    AtCoder ABC 127F Absolute Minima
    AtCoder ABC 127E Cell Distance
    CodeForces 1166E The LCMs Must be Large
    CodeForces 1166D Cute Sequences
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/9134999.html
Copyright © 2011-2022 走看看