java深拷贝与浅拷贝浅析

6 篇文章 0 订阅

java深拷贝浅拷贝


每个技术都有其产生的理由,为什么需要拷贝呢?如何实现拷贝?深拷贝和浅拷贝的区别?如何解决多层克隆问题?

拷贝和赋值的区别
  • 赋值变量复制的是引用,即对象在内存中的地址,a,b对象指向了同一个对象。此时a == b

    • Object a = new Object();
      Object b = a;
      
  • 使用clone拷贝的对象跟原来的对象同时存在,即 a != b

深拷贝浅拷贝的区别
  • 浅拷贝
    • 如果原对象是基本数据类型,复制一份给克隆对象;如果是引用类型,则将对象的地址复制一份给克隆对象,原型对象和克隆对象的成员变量指向相同的内存地址
      • E5puYD
  • 深拷贝
    • 无论是基本数据类型还是引用类型,都将复制一份给克隆对象,对象锁包含的所有成员变量也复制,即多层深拷贝
      • 87JyhW
如何实现拷贝
  • 浅拷贝

    • Object拷贝方法

      • 使用了本地方法,效率高

      • protected native Object clone() throws CloneNotSupportedException;
        
    • 被拷贝的类实现Cloneable接口,并重写clone()方法

      • @Override
            protected Object clone() throws CloneNotSupportedException {
                return super.clone();
            }
        
  • 深拷贝

    • 重写clone()方法

      • 对于原对象中的成员变量是引用类型,则将成员变量中的clone方法重写,调用clone方法
      • 存在的问题:如果引用类型含有多层引用类型,那么使用重写clone方法进行深拷贝很麻烦,可以通过序列化的方法实现
    • 通过序列化(Serializable)实现 (多层引用类型)

      • 序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

      • public class Outer implements Serializable{
          private static final long serialVersionUID = 369285298572941L;  //最好是显式声明ID
          public Inner inner;
         //Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化]
          public Outer myclone() {
              Outer outer = 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);
                  outer = (Outer) ois.readObject();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
              return outer;
          }
        }
        
        //Inner也必须实现Serializable,否则无法序列化:
        public class Inner implements Serializable{
          private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
          public String name = "";
        
          public Inner(String name) {
              this.name = name;
          }
        
          @Override
          public String toString() {
              return "Inner的name值为:" + name;
          }
        }
        
      • 注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值