美文网首页
开发Java这么久还不知深浅?

开发Java这么久还不知深浅?

作者: IT_xiao小巫 | 来源:发表于2020-01-11 17:21 被阅读0次

    实际开发场景中,你可能遇到过复制一个对象,而针对这个对象修改不应该影响被复制的对象,举个例子:

    public Subject getStaticSubject(int subjectTempId, List<Subject> staticSubjects) {
            Subject staticSubject = null;
            for (Subject subject : staticSubjects) {
                if (subject.getId() == subjectTempId) {
                    staticSubject = subject;
                    break;
                }
            }
    
            return staticSubject;
        }
    

    这段代码的意思是从静态Subject对象列表中取出一个匹配的Subject:

    调用一下:

    Subject subject = getStaticSubject(subjectTempId, staticSubjects);
    

    但此时我不想直接用这个Subject,只想用copy它的属性值,看下Subject的定义:

    image.png

    我们这个时候来说说浅拷贝

    浅拷贝:主要在于“浅”字,何为浅? 比如基本数据类型,int、double、byte、boolean、char等,直接复制没问题,因为它们不指向任何内存空间。

    实现Java对象浅拷贝很简单:

    1. 实现Cloneable接口,复写clone方法
    @Override
        public Object clone() {
            Subject subject = null;
            try {
                subject = (Subject) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return subject;
        }
    

    实际使用:

    Subject newSubject = subject.clone();
    

    浅拷贝适用于只复制基本数据类型,修改新对象不影响被复制对象的属性值.

    深拷贝

    有浅必然也有深,前面说到复制基本数据类型没问题,但如果对象里面有包含了其他引用,直接复制会有什么结果? 没错,跟你想的一样:

    修改复制的对象的引用会影响被复制对象的引用

    比如Subject对象包含DynamicData引用,直接浅拷贝,新的Subject实例的DynamicData引用指向的是同一块内存空间,

    怎么办? 同样的我也要为DynamicData对象也开辟一块新的内存空间,DynamicData也要实现同样的浅拷贝:

    @Override
        public Object clone() {
            Subject subject = null;
    
            try {
                subject = (Subject) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
    
            subject.dynamicData = (DynamicData) dynamicData.clone();
            return subject;
        }
    

    这个时候懂了吗? 但这里又有一个问题,假如Subject里面包含了多个引用类型对象,clone方法岂不是要针对每个引用对象都要做一次浅拷贝?如果引用对象里面又包含了引用对象怎么办?

    感觉很晕,这样实现深拷贝太麻烦了。有没有更通用的办法?

    答案是:有的,通过序列化就能实现。可以解决多层拷贝的问题。

    直接上代码:

    /**
         * 深度复制方法,需要对象及对象所有的对象属性都实现序列化.
         */
        public Subject deepCloneSubject() {
            Subject subject = null;
            try {
                // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                // 将流序列化成对象
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                subject = (Subject) ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return subject;
        }
    

    实际使用只要这样:

    Subject newSubject = subject.deepCloneSubject();
    

    就这样我们低成本的实现了对象之间的深拷贝。

    当然你不想写重复的代码,封装成工具类:

    public class CloneUtils {
        @SuppressWarnings("unchecked")
        public static <T extends Serializable> T clone(T obj){
            T cloneObj = null;
            try {
                //写入字节流
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ObjectOutputStream obs = new ObjectOutputStream(out);
                obs.writeObject(obj);
                obs.close();
                
                //分配内存,写入原始对象,生成新对象
                ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(ios);
                //返回生成的新对象
                cloneObj = (T) ois.readObject();
                ois.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cloneObj;
        }
    }
    

    perfect,再也不怕写一大堆set方法了。

    相关文章

      网友评论

          本文标题:开发Java这么久还不知深浅?

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