Java 深度解析clone方法

 在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说, A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。因为java中的赋值语句采用的是值传递,即多个栈空间(属性名)引用同一片堆空间内存,对其中一个属性修改,势必影响到所有引用该堆空间对象的属性。

 要满足这种需求虽然有很多途径,但采用clone()方法是其中最简单,也是最高效的手段.。

protected native Object clone() throws CloneNotSupportedException;

 clone方法是Object类中的一个方法,可以通过clone进行对象的拷贝操作。

1. Cloneable接口

 如果想要使用clone方法,单纯的覆写Object的clone方法是不能使用的,他会抛出一个CloneNotSupportedException异常。

 要想正确使用,该对象的类要实现一个Cloneable标识接口。

public interface Cloneable {
}

 Cloneable接口中没有任何方法,它存在的意义就是允许其实现类使用clone方法。所以正确的使用流程应该是这样的:

class Person implements Cloneable {

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

}

 Cloneable接口位于java.lang 包中,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。

2. clone和new的区别

 new 操作符的本意是在堆上开辟内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵(属性名)这个对象。

 clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后, clone 方法返回,一个新的相同的对象被创建,这时我们就可以在外部对其进行访问。

 在前面clone方法的定义中我们可以看到,clone是一个native本地方法,native方法的效率一般来说都是远高于java中的非native方法。所以一般我们使用clone方法,直接调用super.clone()即可。

3. clone方法的使用

class Person implements Cloneable {
    public int age;
    public String name;

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

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

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}


public class Demo {

    public static void main(String[] args) {
        Person person1 = new Person(10, "xucc");
        Person person2 = null;
        try {
            person2 = (Person) person1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person1.age = 9;
        person1.name = "licc";
        System.out.println(person1);
        System.out.println(person2);
    }
}

 运行结果:

com.xucc.exercise.Person@282ba1e
Person{age=9, name='licc'}
com.xucc.exercise.Person@30c7da1e
Person{age=10, name='xucc'}

 我们可以看出,clone确实开辟了新的空间,并且得到了与原有对象相同的值。

 最后,我们应该注意的是,clone()虽然创建了新的对象开辟了新的内存空间,但是对象中包含的一些属性,并没有进行重新创建,例如字符串,还是会从共享池中复用,所以clone属于浅拷贝。

 关于进一步的分析会在之后的文章中展现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值