Java中克隆的用法,深拷贝、浅拷贝概念的引出

一 什么是克隆

克隆就是根据已有对象复制出另一个对象。比如用A克隆出B,一般在java中有如下约定:

// A与B的引用不同
A!=B
// A与B的类相同
A.getClass == B.getClass
// A和B内容相同
A.equals(B)

通常来说 A.equals(B) == true,但是这不是强制的要求,开发人员可根据具体需要决定是否重写equals方法。

二 怎么实现克隆

需要克隆的类 实现 Cloneable 接口,并重写其中的clone方法。其中super.clone()会把原始对象的属性克隆到新对象的属性中。

public class Student1 implements Cloneable{
    private String name;
    private int age;

    public Student1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Student1 clone() throws CloneNotSupportedException {
        return (Student1)super.clone();
    }
}

测试程序如下:

public class TestClone {
    public static void main(String[] args) {
        Student1 xiaoming = new Student1(new String("小明"),12);
        Student1 xiaoming_Copy = null;
        try {
            xiaoming_Copy = xiaoming.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(xiaoming.toString());
        System.out.println(xiaoming_Copy.toString());
    }
}

输出:

{“age”:12,”name”:”小明”}
{“age”:12,”name”:”小明”}

这样Student的克隆成功了。

实际上,Cloneable是个空接口,它没有定义任何成员。它只是一个对象可重写并使用clone方法的标志。

public interface Cloneable {
}

三 这样就结束了吗??

如果一个类中的域(属性)都是基本数据类型或者final类型当然就没有问题了,但是如果一个类的域是非final类型的引用类型(比如:数组、其他对象)。那么这样克隆就会出现一个问题,先看一个例子:
新加了一个Teacher类:

public class Teacher {
    private String name;
    private int age;

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

Stuent类中加入Teacher属性:

public class Student2 implements Cloneable {
    private String name;
    private int age;
    private Teacher teacher;

    public Student2(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }

    @Override
    protected Student2 clone() throws CloneNotSupportedException {
        return (Student2)super.clone();
    }

运行下面的测试程序:

public class TestClone {
    public static void main(String[] args) {
        Teacher teacher = new Teacher("王尼玛",41);
        Student2 xiaoming = new Student2(new String("小明"),12,teacher);
        Student2 xiaoming_Copy = null;
        try {
            xiaoming_Copy = xiaoming.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        xiaoming_Copy.getTeacher().setName("齐东强");
        System.out.println(xiaoming.toString());
        System.out.println(xiaoming_Copy.toString());
    }
}

输出

{“age”:12,”name”:”小明”,”teacher”:{“age”:41,”name”:”齐东强”}}
{“age”:12,”name”:”小明”,”teacher”:{“age”:41,”name”:”齐东强”}}

发现拷贝对象副本在改变自己属性的时候会影响原始主本的数据,这是因为super.clone()只会把Student中对Teacher的引用值copy给克隆对象。所以主本和副本中的teacher对象指向了相同的堆地址。显然这不是我们想要的。

四 如何解决

嵌套多少层非final类型的引用,我们就拷贝多少层!
Teacher也 实现 Cloneable类,添加clone方法

public class Teacher implements Cloneable{
    private String name;
    private int age;

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Teacher clone() throws CloneNotSupportedException {
        return (Teacher)super.clone();
    }
 }

修改Student中的clone方法为:

@Override
    public Student2 clone() throws CloneNotSupportedException {
        Student2 copy = (Student2)super.clone();
        copy.teacher = teacher.clone();
        return copy;
    }

再次运行测试程序

{“age”:12,”name”:”小明”,”teacher”:{“age”:41,”name”:”王尼玛”}}
{“age”:12,”name”:”小明”,”teacher”:{“age”:41,”name”:”齐东强”}}
这次修改克隆对象的引用属性,原对象就不会被改变了

五 总结

其实三中的demo被称为浅克隆(浅拷贝),四中的解决方案为深克隆(深拷贝)

5.1 浅拷贝

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的非final引用类型属性指向的对象

public Object clone() {
       return super.clone();
    }

直接使用super.clone()方法。

5.2 深拷贝

深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象

public Object clone() {
       Object obj = super.clone();//先浅拷贝一下
       //然后修正需要修正的属性
       obj.属性1 = xxx;
       obj.属性2 = xxx;
       ...
    }

5.3 如何选择使用哪种拷贝方式

1.如果类中的属性只包含有基本数据类型常量(包括String),那么这个类使用浅拷贝。

使用浅拷贝的类也可能需要修正,譬如代表序列号、其他唯一ID、对象的创建时间的属性,不管这些属性是基本类型还是常量,都需要修正。(常量需要修正时,不能使用super.clone()

2.如果类中的属性包含有非final的引用类型时(String不算),使用深拷贝,不然拷贝对象的改变会影响原对象,就失去了拷贝的意义。

5.4 什么时候使用克隆

  1. 当你想获取某个对象的数据内容,对其进行一系列操作又不想改变其内容时。
  2. 直接用构造器新生成一个对象的代价太大时。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值