美文网首页
JAVA对象拷贝该使用哪种方式?

JAVA对象拷贝该使用哪种方式?

作者: 琥珀光 | 来源:发表于2019-11-21 19:32 被阅读0次

    背景

    在Java中会遇到很多对象拷贝的情况,用的时候比较随意,一般直接使用Beautils的copy方法,图简单方便,但是经过测试后发现实际效率真的千差万别

    众所周知,拷贝分为浅拷贝和深拷贝,我认为浅拷贝并不是真正意义的拷贝,所以本文的对象拷贝均为深拷贝

    如果想直接看结论,直接滑动到底部

    拷贝方式

    Java对象拷贝目前已经的方式有四种方式:

    • Bean对象的Setter方式
    • 继承覆盖clone方法
    • BeanUtils方式
    • Java本身序列化方式

    实施测试

    该选择哪种?本文用了JMH压测工具做了比较,为了避免简单对象影响测试结果,我使用了稍微复杂点的对象做了测试,至于JMH如何使用,参考JMH: 最装逼,最牛逼的基准测试工具套件

    测试对象的设计是这样的:里面有一些基本类型,最重要的是有一个对象,对象里面又是一个复合对象,不算复杂,也并不简单,满足日常需求的情况

    测试对象如下:

    package org.sample;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * Default Note
     *
     * @author liupz@glodon.com
     * @version 1.0
     * @date 2019/11/21 16:59
     */
    public class ComplexObject implements Cloneable{
        private int number;
    
    
        private String string;
    
    
        private long time;
    
    
        private short st;
    
    
        private double dd;
    
    
        private Date date;
    
        private Person person;
    
    
        public int getNumber() {
            return number;
        }
    
        public void setNumber(int number) {
            this.number = number;
        }
    
        public String getString() {
            return string;
        }
    
        public void setString(String string) {
            this.string = string;
        }
    
        public long getTime() {
            return time;
        }
    
        public void setTime(long time) {
            this.time = time;
        }
    
        public short getSt() {
            return st;
        }
    
        public void setSt(short st) {
            this.st = st;
        }
    
        public double getDd() {
            return dd;
        }
    
        public void setDd(double dd) {
            this.dd = dd;
        }
    
        public Date getDate() {
            return date;
        }
    
        public void setDate(Date date) {
            this.date = date;
        }
    
        public Person getPerson() {
            return person;
        }
    
        public void setPerson(Person person) {
            this.person = person;
        }
    
        public static class Person implements Cloneable{
            private int age;
            private String username;
            private String password;
    
            private List<Person> sons;
    
            public int getAge() {
                return age;
            }
    
            public void setAge(int age) {
                this.age = age;
            }
    
            public String getUsername() {
                return username;
            }
    
            public void setUsername(String username) {
                this.username = username;
            }
    
            public String getPassword() {
                return password;
            }
    
            public void setPassword(String password) {
                this.password = password;
            }
    
            public List<Person> getSons() {
                return sons;
            }
    
            public void setSons(List<Person> sons) {
                this.sons = sons;
            }
            @Override
            public Object clone() {
    
                Person person = null;
                try{
                    person = (Person)super.clone();   //浅复制
                }catch(CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                person.sons = new ArrayList<>();
                for(Person person1:sons){
                    ComplexObject.Person s1 = new ComplexObject.Person();
                    s1.setSons(person1.getSons());
                    s1.setAge(person1.getAge());
                    s1.setPassword(person1.getPassword());
                    s1.setUsername(person1.getUsername());
    
                    person.sons.add(s1);
                }
    
                return person;
            }
    
        }
    
        @Override
        public Object clone() {
    
            ComplexObject complexObject = null;
            try{
                complexObject = (ComplexObject)super.clone();   //浅复制
            }catch(CloneNotSupportedException e) {
                e.printStackTrace();
            }
            complexObject.person = (Person) person.clone();   //深度复制
            return complexObject;
        }
    }
    

    对于压测类,我在压测类构建的时候初始化了一个复杂对象,然后在压测方法中测试了四种情况,压测类的代码如下:

    /*
     * Copyright (c) 2014, Oracle America, Inc.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     *  * Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     *
     *  * Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     *  * Neither the name of Oracle nor the names of its contributors may be used
     *    to endorse or promote products derived from this software without
     *    specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     * THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    package org.sample;
    
    import org.openjdk.jmh.annotations.*;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
    @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
    @Fork(1)
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    @State(Scope.Benchmark)
    public class MyBenchmark {
    
        private ComplexObject complexObject;
    
        /*序列化方式的时候放开注释*/
    //    private ComplexObjectSerializer complexObject;
    
    
        public MyBenchmark(){
            complexObject = new ComplexObject();
    
            complexObject.setDate(new Date());
            complexObject.setDd(1111);
            complexObject.setNumber(222);
            complexObject.setSt((short) 1.0);
            complexObject.setString("XXXXXXXXXXXX");
    
            complexObject.setTime(System.currentTimeMillis());
    
            List<ComplexObject.Person> sons = new ArrayList<>();
            ComplexObject.Person  s1 = new ComplexObject.Person();
            s1.setAge(11);
            s1.setPassword("XXXXXXXXXXXXXXX");
            s1.setUsername("AAAAAAAAAAAAAAAAA");
            s1.setSons(null);
            sons.add(s1);
    
    
            ComplexObject.Person  s2 = new ComplexObject.Person();
            s2.setAge(11);
            s2.setPassword("XXXXXXXXXXXXXXX");
            s2.setUsername("AAAAAAAAAAAAAAAAA");
            s2.setSons(null);
            sons.add(s2);
    
            ComplexObject.Person  s3 = new ComplexObject.Person();
            s3.setAge(11);
            s3.setPassword("XXXXXXXXXXXXXXX");
            s3.setUsername("AAAAAAAAAAAAAAAAA");
            s3.setSons(null);
            sons.add(s3);
    
    
            ComplexObject.Person  person = new ComplexObject.Person();
            person.setAge(11);
            person.setPassword("XXXXXXXXXXXXXXX");
            person.setUsername("AAAAAAAAAAAAAAAAA");
            person.setSons(sons);
    
            complexObject.setPerson(person);
        }
    
        @Benchmark
        public void testMethod() {
            // This is a demo/sample template for building your JMH benchmarks. Edit as needed.
            // Put your benchmark code here.
    //        Student stu1 = new Student();
    //        stu1.setNumber(12345);
    //        Student stu2 = new Student();
    //        try {
    //            BeanUtils.copyProperties(stu1, stu2);
    //        } catch (IllegalAccessException e) {
    //            e.printStackTrace();
    //        } catch (InvocationTargetException e) {
    //            e.printStackTrace();
    //        }
            /**
             * 1.通过set方法拷贝对象
             */
    //        ComplexObject complexObjects = new ComplexObject();
    //
    //        ComplexObject.Person person = new ComplexObject.Person();
    //        person.setUsername(complexObject.getPerson().getUsername());
    //        person.setPassword(complexObject.getPerson().getPassword());
    //        person.setAge(complexObject.getPerson().getAge());
    //
    //        List<ComplexObject.Person> sons = new ArrayList<>();
    //
    //        for(ComplexObject.Person son:complexObject.getPerson().getSons()){
    //            ComplexObject.Person s1 = new ComplexObject.Person();
    //            s1.setSons(son.getSons());
    //            s1.setAge(son.getAge());
    //            s1.setPassword(son.getPassword());
    //            s1.setUsername(son.getUsername());
    //            sons.add(s1);
    //        }
    //
    //        person.setSons(sons);
    //
    //
    //        complexObjects.setPerson(person);
    //        complexObjects.setTime(complexObject.getTime());
    //        complexObjects.setString(complexObject.getString());
    //        complexObjects.setSt(complexObject.getSt());
    //        complexObjects.setDd(complexObject.getDd());
    //        complexObjects.setNumber(complexObject.getNumber());
    //        complexObjects.setDate(complexObject.getDate());
    
    
            /**
             * 克隆方式
             */
    //        ComplexObject complexObjects = (ComplexObject) complexObject.clone();
    
    
            /**
             * BeanUtils方式
             */
    //        ComplexObject complexObjects = new ComplexObject();
    //        try {
    //            BeanUtils.copyProperties(complexObjects,complexObject);
    //        } catch (IllegalAccessException e) {
    //            e.printStackTrace();
    //        } catch (InvocationTargetException e) {
    //            e.printStackTrace();
    //        }
    
            /**
             * 序列化深拷贝
             */
            ComplexObject complexObjects = (ComplexObject)complexObject.clone();
    
    
        }
    
    }
    
    

    测试结果

    分别修改压测方法,得到的测试结果如下:

    拷贝方式 测试结果
    Bean对象的Setter方式
    继承覆盖clone方法
    BeanUtils方式
    Java本身序列化方式

    由此可以看出:
    Bean对象的Setter方式最优,Java本身序列化方式大概是Setter方式 的500倍! BeanUtils方式 也非常低效,大概是Setter方式的100多倍,所以以前为了方便而直接使用BeanUtils的方式可以在效率不敏感的代码中使用,但是绝不能在高频程序中用,最后看一下继承覆盖clone方法,与set方式相差无几,但是set方式几乎是用到的时候都要写一遍,clone方式只需要实现cloneable接口就行,只写一次就能在任何用到这个对象拷贝的地方使用

    结论

    所以最终结论:千万别为了省事使用第三方库,除非你的程序不是高频调用的,强烈建议自己实现clone方法实现对象拷贝

    参考资料

    Java对象的复制三种方式

    JMH: 最装逼,最牛逼的基准测试工具套件

    Java对象的快速复制的几种方式

    相关文章

      网友评论

          本文标题:JAVA对象拷贝该使用哪种方式?

          本文链接:https://www.haomeiwen.com/subject/gqztwctx.html