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
  • 相关阅读:
    NGUI UIEventListener
    Unity3d NGUI Panel 滑动菜单
    NGUI 密码输入框
    Unity3d 时间差
    Unity3d 添加组件脚本和建菜单
    c# [HideInInspector] 属性
    c# [System.Serializable]
    Activity 生命周期
    Unity3d OnApplicationPause与OnApplicationFocus
    C# 中 Struct 与 Class 的区别
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/9134999.html
Copyright © 2011-2022 走看看