Java 深度解析clone方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40739833/article/details/81279017

 在实际编程过程中,我们常常要遇到这种情况:有一个对象 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属于浅拷贝。

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

猜你喜欢

转载自blog.csdn.net/weixin_40739833/article/details/81279017