Java-深复制|浅复制 (深拷贝|浅拷贝)XXOO

描述

1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。

2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

在 Java 中,所有的 Class 都继承自 Object ,而在 Object 上,存在一个 clone() 方法,

它被声明为了 protected ,所以我们可以在其子类中,使用它。而无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。

代码实践

public static void main(String[] args) {
        FathClass fathClassA = new FathClass();
        fathClassA.name = "张三";
        fathClassA.age= 19;
        fathClassA.childClass = new ChildClass();
        fathClassA.childClass.cName = "李四";
        fathClassA.childClass.cAge = 20;

        FathClass fathClassB = (FathClass)fathClassA.clone();

        // 深拷贝
        log.info("fathClassA == fathClassB : {}", fathClassA == fathClassB);
        log.info("fathClassA hashCode : {}", fathClassA.hashCode());
        log.info("fathClassB hashCode : {}", fathClassB.hashCode());
        log.info("fathClassA.name : {}", fathClassA.name);
        log.info("fathClassB.name : {}", fathClassB.name);
        log.info("-------------------------------------------------------");
        // 浅拷贝
        log.info("A.childClass == B.childClass : {}", fathClassA.childClass == fathClassB.childClass);
        log.info("A.childClass hashCode : {}", fathClassA.childClass.hashCode());
        log.info("B.childClass hashCode : {}", fathClassB.childClass.hashCode());

    }

  
    static class FathClass implements Cloneable{

        public String name;

        public int age;

        ChildClass childClass;

        @Override
        protected Object clone() {
            try {
                // 浅拷贝
                FathClass fathClone = (FathClass) super.clone();
                fathClone.childClass = (ChildClass) this.childClass.clone();
                return fathClone;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    static class ChildClass implements Cloneable{

        public String cName;

        public int cAge;

        @Override
        protected Object clone() {
            try {
                // 浅拷贝
                return super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
深拷贝比较常用的方案有两种: 
序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。 
继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。 
总结:
实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
但是如果其引用数据类型也使用了clone() 方法,取到的就是这个对象的深拷贝,对于引用数据类型对象是浅拷贝。
1.System.arraycopy()的理解
* public static native void arraycopy(
*  Object src,   //源数组
*  int  srcPos,  //源数组的起始位置
*  Object dest,  //目标数组
*  int destPos,  //目标数组的起始位置
*  int length);  //复制长度

深复制还是浅复制?线程安全?还是不安全高效还是低效?

 

        /**
         * 1.1.深复制还是浅复制
         */
        // 初始化对象数组
        User[] users = new User[]{
                new User(1, "seven", "[email protected]"),
                new User(2, "six", "[email protected]"),
                new User(3, "ben", "[email protected]")};
        // 新建一个目标对象数组
        User[] target = new User[users.length];
        // 实现复制
        System.arraycopy(users, 0, target, 0, users.length);
        // 判断
        log.info("源对象与目标对象的物理地址是否一样:" + (users[0] == target[0] ? "浅复制" : "深复制"));
        target[0].setEmail("[email protected]");
        log.info("修改目标对象的属性值后源对象users:");
        for (User user : users) {
            log.info(user.toString());
        }

        /**
         * 1.2.线程安全,还是不安全
         */
        ArrayCopyThreadSafe threadSafe = new ArrayCopyThreadSafe();
        try {
            threadSafe.doThreadSafeCheck();
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 1.3.高效还是低效
         */
        String[] srcArray = new String[1000000];
        String[] forArray = new String[srcArray.length];
        String[] arrayCopyArray  = new String[srcArray.length];

        //初始化数组
        for(int index  = 0 ; index  < srcArray.length ; index ++){
            srcArray[index] = String.valueOf(index);
        }

        long forStartTime = System.currentTimeMillis();
        for(int index  = 0 ; index  < srcArray.length ; index ++){
            forArray[index] = srcArray[index];
        }
        long forEndTime = System.currentTimeMillis();
        log.info("for方式复制数组耗时(单位毫秒):"  + (forEndTime - forStartTime));

        long arrayCopyStartTime = System.currentTimeMillis();
        System.arraycopy(srcArray,0,arrayCopyArray,0,srcArray.length);
        long arrayCopyEndTime = System.currentTimeMillis();
        log.info("System.arraycopy复制数组耗时(单位毫秒):"  + (arrayCopyEndTime - arrayCopyStartTime));


    }

    static class User {
        private Integer id;
        private String username;
        private String email;

        // 无参构造函数
        public User() {
        }

        // 有参的构造函数
        public User(Integer id, String username, String email) {
            super();
            this.id = id;
            this.username = username;
            this.email = email;
        }

        public Integer getId() {
            return id;
        }

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

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        @Override
        public String toString() {
            return "User [id=" + id + ", username=" + username + ", email=" + email + "]";
        }
    }

    static class ArrayCopyThreadSafe {
        private  int[] arrayOriginal = new int[1024 * 1024 * 10];
        private  int[] arraySrc = new int[1024 * 1024 * 10];
        private  int[] arrayDist = new int[1024 * 1024 * 10];
        private ReentrantLock lock = new ReentrantLock();

        private void modify() {
            for (int i = 0; i < arraySrc.length; i++) {
                arraySrc[i] = i + 1;
            }
        }

        private void copy() {
            System.arraycopy(arraySrc, 0, arrayDist, 0, arraySrc.length);
        }

        private void init() {
            for (int i = 0; i < arraySrc.length; i++) {
                arrayOriginal[i] = i;
                arraySrc[i] = i;
                arrayDist[i] = 0;
            }
        }

        private void doThreadSafeCheck() throws Exception {
            for (int i = 0; i < 100; i++) {
                log.info("run count: " + (i + 1));
                init();
                Condition condition = lock.newCondition();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        lock.lock();
                        try{
                            condition.signalAll();
                        }catch (Exception e){

                        }finally {
                            lock.unlock();
                            copy();
                        }
                    }
                }).start();

                lock.lock();
                try{
                    // 这里使用 Condition 来保证拷贝线程先已经运行了.
                    condition.await();
                }catch (Exception e){

                }finally {
                    lock.unlock();
                }

                Thread.sleep(2); // 休眠2毫秒, 确保拷贝操作已经执行了, 才执行修改操作.
                modify();

                // System.arraycopy 是线程不安全的, 那么 arrayOriginal 不等于 arrayDist.
                if (!Arrays.equals(arrayOriginal, arrayDist)) {
                    throw new RuntimeException("System.arraycopy is not thread safe");
                }
            }
        }

    }

总结:

 1.System.arraycopy() 在拷贝数组的时候,采用的使用浅复制,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。

2.System.arraycopy 是线程不安全的。

3.当测试数组的范围比较小的时候,两者相差的时间无几,当测试数组的长度达到百万级别, System.arraycopy的速度优势就开始体现了,根据对底层的理解,System.arraycopy是对内存直接进行复制,减少了for循环过程中的寻址时间,从而提高了效能。

以上 

おすすめ

転載: blog.csdn.net/qq_35731570/article/details/109355712