Java深拷贝、浅拷贝

浅拷贝、深拷贝的概念

在 Java 中,除了基本数据类型(元类型)之外,还存在“类的实例对象”这个引用数据类型。而一般使用“ = ”号做赋值操作的时候,对于基本数据类型,实际上是拷贝的它的值;但是对于对象而言,赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

所谓的浅拷贝和深拷贝,是在拷贝对象的时候,对“类的实例对象”这种引用数据类型的不同操作。对于基本数据类型,赋值的就是值,不存在深拷贝、浅拷贝的问题。

浅拷贝的实现

Java中的clone()

clone()方法是一个本地方法,实现的是浅拷贝。调用该方法的前提如下。否则就会抛出错误。

  • 实现java.lang.Cloneable接口
  • 或)其某个父类实现Cloneable接口
  • 或)实现了Cloneable的子接口,

clone()进行的是浅复制(shallow copy)。clone()会将被复制实例的字段值直接复制到新的实例中,并不考虑字段中实际所保存的内容。clone()在进行复制时也不会调用被复制实例的构造函数。在使用时要覆写clone(),可以通过覆写clone()来实现自己的copy逻辑。

clone()的执行过程

  1. 1.分配与要复制的实例同样大小的内存空间;
  2. 2.将要复制的实例中的字段的值复制到所分配的内存空间中去。

 Cloneable是一个标记接口(marker interface),其并没有声明任何方法,只是用来标记 “可以使用clone方法进行复制”。

测试案例

 定义两个类,父类的成员包括子类。

 对比结果,发现赋值后的父类对象与原对象的hashcode不同,说明内存地址是不一样。而对于对象里的name和子类对象,hashcode是不同的,前者是字符串常量池,后者是符号引用的原因。拷贝的过程中,在新对象的内存地址中,对应的位置存储的是子类对象在堆中的地址。两个父类对象指向的相同的子类对象。这就是浅拷贝。

如何进行深拷贝

比较常用的方案有两种:

  1. 序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象。
  2. 继续利用 clone() 方法,对其内的引用类型的变量,再进行一次 clone()。

利用clone()方法实现

改写子类,让它也实现clone()方法。

 改写父类的clone()方法,对子类执行子类的clone()方法

 再次执行,子类对象也拷贝了

分析:该案例对于子类而言进行了一次浅拷贝;对于父类而言进行了一次深拷贝。如果子类还有引用类型,可以继续这样做来实现。

使用序列化反序列化实现

    public  void main() throws IOException {
        TestFatherClass father1 = new TestFatherClass();
        father1.fatherAge = 50;
        father1.fatherName = "一号";
        father1.children = new TestChildrenClass();
        father1.children.childrenName = "小一号";
        father1.children.childrenAge = 20;
        TestFatherClass father2;
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("XXX\\iotest.txt"));
            oos.writeObject(father1);
            oos.flush();
        }catch (IOException e){
            throw new RuntimeException(e);
        }finally {
            try {
                oos.close();
            }catch (IOException e){
                throw new RuntimeException(e);
            }
        }
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("XXX\\iotest.txt"));
            father2 = (TestFatherClass) ois.readObject();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                ois.close();
            }catch (IOException e)  {
                throw new RuntimeException(e);
            }
        }
        System.out.print("1==2:" + (father1 == father2) + "\n");
        System.out.print("1.hashcode:" + father1.hashCode() + "\n");
        System.out.print("2.hashcode:" + father2.hashCode() + "\n");
        System.out.print("----------" + "\n");
        System.out.print("children1==children2:" + (father1.children == father2.children) + "\n");
        System.out.print("children1.hashcode:" + father1.children.hashCode() + "\n");
        System.out.print("children2.hashcode:" + father2.children.hashCode() + "\n");
    }

输出为

Java clone()方法 与 Cloneable接口详解

java深拷贝与浅拷贝

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值