JAVA 浅拷贝和深拷贝

一.对象拷贝

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部 数据。Java中有三种类型的对象拷贝:

  • 浅拷贝(Shallow Copy)
  • 深拷贝(Deep Copy)
  • 延迟拷贝(Lazy Copy)

1. 浅拷贝 Shallow Copy

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

这里写图片描述

代码示例:

public class Subject {

    private String name;

    public Subject(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Student implements Cloneable {

    //引用类型
    private Subject subject;

    //基本类型
    private String name;

    public Student(String name, String subjectName) {
        this.name = name;
        subject = new Subject(subjectName);
    }

    public Subject getSubject() {
        return subject;
    }

    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            // 直接调用父类的clone()方法
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}
public class ShallowCopyTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        /*原始对象*/
        System.out.println("==============Source================");
        Student sourceStu = new Student("stuName", "subjectName");
        System.out.println("Source对象的Name: " + sourceStu.getName());
        System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());

        /*拷贝对象*/
        System.out.println("================Clone===============");
        Student cloneStu = (Student) sourceStu.clone();
        System.out.println("Clone对象的Name: " + cloneStu.getName());
        System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());

        /*修改克隆对象的基本数据类型变量 源对象的该变量不会发生改变*/
        System.out.println("================Modify===============");
        cloneStu.setName("cloneName");
        System.out.println("Clone对象的Name: " + cloneStu.getName());
        System.out.println("Source对象的Name: " + sourceStu.getName());

         /*修改克隆对象的引用类型变量 源对象的该变量会发生改变*/
        cloneStu.getSubject().setName("cloneSubjectName");
        System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
        System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());
    }
}

Student类里有一个基本数据类型的name和引用数据类型的subject
当使用浅克隆时,修改克隆对象的基本数据类型变量,源对象的该变量不会发生改变 而引用数据类型变量会随之改变

输出结果:

==============Source================
Disconnected from the target VM, address: '127.0.0.1:64518', transport: 'socket'
Source对象的Name: stuName
Source对象的Subject的Name: subjectName
================Clone===============
Clone对象的Name: stuName
Clone对象的Subject的Name: subjectName
================Modify===============
Clone对象的Name: cloneName
Source对象的Name: stuName
Clone对象的Subject的Name: cloneSubjectName
Source对象的Subject的Name: cloneSubjectName

二. 深拷贝 Deep Copy

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

这里写图片描述

代码示例:

Subject类和Student类和上例相同 仅仅修改了Student类内的clone方法

@Override
    protected Object clone() throws CloneNotSupportedException {
        /*深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立*/
        return new Student(getName(), getSubject().getName());
    }
public class DeepCopyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
  /*原始对象*/
        System.out.println("==============Source================");
        Student sourceStu = new Student("stuName", "subjectName");
        System.out.println("Source对象的Name: " + sourceStu.getName());
        System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());

        /*拷贝对象*/
        System.out.println("================Clone===============");
        Student cloneStu = (Student) sourceStu.clone();
        System.out.println("Clone对象的Name: " + cloneStu.getName());
        System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());

        /*修改克隆对象的基本数据类型变量 源对象的该变量不会发生改变*/
        System.out.println("================Modify===============");
        cloneStu.setName("cloneName");
        System.out.println("Clone对象的Name: " + cloneStu.getName());
        System.out.println("Source对象的Name: " + sourceStu.getName());

         /*修改克隆对象的引用类型变量 源对象的该变量会发生改变*/
        cloneStu.getSubject().setName("cloneSubjectName");
        System.out.println("Clone对象的Subject的Name: " + cloneStu.getSubject().getName());
        System.out.println("Source对象的Subject的Name: " + sourceStu.getSubject().getName());

    }
}

输出结果

==============Source================
Disconnected from the target VM, address: '127.0.0.1:64577', transport: 'socket'
Source对象的Name: stuName
Source对象的Subject的Name: subjectName
================Clone===============
Clone对象的Name: stuName
Clone对象的Subject的Name: subjectName
================Modify===============
Clone对象的Name: cloneName
Source对象的Name: stuName
Clone对象的Subject的Name: cloneSubjectName
Source对象的Subject的Name: subjectName

使用序列化进行深拷贝

代码示例:

public class Circle implements Serializable {
    private int x;
    private int y;

    public Circle(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}
public class SerializableDeepCopyTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;

        Circle sourceCircle = new Circle(400, 300);

        /*写入Object*/
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bao);
        oos.writeObject(sourceCircle);
        oos.flush();

        /*读取Object*/
        ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
        ois = new ObjectInputStream(bai);
        Circle cloneCircle = (Circle) ois.readObject();

        System.out.println("sourceCircle == cloneCircle:" + (sourceCircle == cloneCircle));
        cloneCircle.setX(1000);
        cloneCircle.setY(2000);
        System.out.println("sourceCircle" + sourceCircle);
        System.out.println("cloneCircle" + cloneCircle);
    }

}

输出结果:

sourceCircle == cloneCircle:false
sourceCircleCircle{x=400, y=300}
cloneCircleCircle{x=1000, y=2000}

三. 延迟拷贝

延迟拷贝是浅拷贝和深拷贝的一个组合,实际上很少会使用。 当最开始拷贝一个对象时,会使用速度较快的浅拷贝,还会使用一个计数器来记录有多少对象共享这个数据。当程序想要修改原始的对象时,它会决定数据是否被共享(通过检查计数器)并根据需要进行深拷贝。
延迟拷贝从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度。当原始对象中的引用不经常改变的时候可以使用延迟拷贝。由于存在计数器,效率下降很高,但只是常量级的开销。而且, 在某些情况下, 循环引用会导致一些问题。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值