java源码分析-Object类clone方法

java源码分析-Object类clone方法

先看一下源码:

/**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

通过源码和注释我们注意到以下几点:

1、Object类中的clone()方法是一个native方法,即本地方法;

2、Object类中的clone()方法被protected修饰,被子累重写后该方法的属性变为public;

3、Object类中的clone()方法返回一个Object对象,也就是说我们必须进行强制类型转换才能得到我们需要的类型。

那么clone方法到底是什么功能呢?

​ 其实简单说,clone方法就是复制一个对象并返回。那么这个赋值的对象与元对象有什么关联呢?在搞清楚clone方法的原理之前,我们先来了解一下两个概念:深拷贝浅拷贝

深拷贝和浅拷贝

深拷贝

​ 被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象引用的对象都复制一遍。

​ 我们用JVM内存图理解一下:

我们定义一个Person类,它含有两个成员变量age,name:

class Person {
    private int age;
    private String name;

    public Person() {
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

​ Person类中含有两个成员变量,一个是基本类型age,根据定义基本类型的拷贝直接进行值拷贝,而name是引用类型String,进行也需要赋值新的对象,就相当于新new String(“zhangsan”)这个对象。所以当我们进行深拷贝时,实际内存情况如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w2hjpIZI-1613718350080)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219090510410.png)]

浅拷贝

​ 被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。

​ 进行浅拷贝时,基本类型直接对值进行拷贝,而对引用类型进行拷贝是实际是只进行了引用的复制,即不同的引用指向相同的对象,内存分布如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i6naxC2i-1613718350082)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219090351521.png)]

clone方法是深拷贝or浅拷贝

我们用实例来验证:

package test.java.lang;

/**
 * 分析Object类的clone方法
 */
public class ObjectCloneDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        PersonDemo p1 = new PersonDemo("张三",18);
        PersonDemo p2 = (PersonDemo) p1.clone();
        System.out.println(p1 == p2);
        System.out.println(p1.getName() == p2.getName());
    }
}

class PersonDemo implements Cloneable {

    private String name;

    private int age;

    public PersonDemo() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

打印结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmznwdAb-1613718350085)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210218205835658.png)]

p1.getName() == p2.getName()返回true,说明这是一个浅拷贝啊,只是复制了引用。对于PersonDemo类中有int基本类型成员,直接将一个4字节的整数值拷贝过来就行。在clone方法调用时,会复制PersonDemo对象,用于拷贝基本数据,对于String类型的name字段来说,它实际指向一个String对象,那么复制的时候有两种方式,一种直接复制引用指向相同的对象;另一种是赋值一个新对象,复制引用指向这个新对象;很明显Object类的clone方法采用了第一种方式,即浅拷贝的方式。

重写clone方法实现深拷贝

​ 我们知道了clone方法是浅拷贝方式,那么有没有办法实现深拷贝呢?

​ 是有的, 我们需要实现Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:

package test.java.lang;

/**
 * 分析Object类的clone方法
 */
public class ObjectCloneDemo2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        BodyDemo body = new BodyDemo();
        PersonDemo2 p1 = new PersonDemo2(body, 18);
        PersonDemo2 p2 = (PersonDemo2) p1.clone();
        System.out.println(p1 == p2);
        System.out.println(p1.getBody() == p2.getBody());
    }
}

class PersonDemo2 implements Cloneable {

    private BodyDemo body;

    private int age;

    public PersonDemo2() {
    }

    public PersonDemo2(BodyDemo body, int age) {
        this.body = body;
        this.age = age;
    }

    public BodyDemo getBody() {
        return body;
    }

    public void setBody(BodyDemo body) {
        this.body = body;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

class BodyDemo{

}

这段代码和之前几乎没有什么区别,我们也能知道结果,由于clone是浅拷贝,那么结果是false、true;

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98NWblcP-1613718350088)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219140532297.png)]

那么要实现深拷贝,对于引用类型的成员变量就需要该类型实现实现Clonable接口,并覆盖并实现clone方法;

实现Cloneable接口

首先,看一下源码:

1 public interface Cloneable { 
2 }

Cloneable接口是一个空接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中 clone()方法的,如果 clone 类没有实现 Cloneable 接口,并调用了 Object 的 clone() 方法(也就是调用了 super.Clone() 方法),那么Object 的 clone() 方法就会抛出 CloneNotSupportedException 异常。

我们修改一下代码:

package test.java.lang;

/**
 * 分析Object类的clone方法
 */
public class ObjectCloneDemo2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        BodyDemo body = new BodyDemo();
        PersonDemo2 p1 = new PersonDemo2(body, 18);
        PersonDemo2 p2 = (PersonDemo2) p1.clone();
        System.out.println(p1 == p2);
        System.out.println(p1.getBody() == p2.getBody());
    }
}

class PersonDemo2 implements Cloneable {

    private BodyDemo body;

    private int age;

    public PersonDemo2() {
    }

    public PersonDemo2(BodyDemo body, int age) {
        this.body = body;
        this.age = age;
    }

    public BodyDemo getBody() {
        return body;
    }

    public void setBody(BodyDemo body) {
        this.body = body;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {	
        PersonDemo2 personDemo2 = (PersonDemo2) super.clone();	
        personDemo2.body = (BodyDemo) body.clone();//需要调用body的clone方法
        return personDemo2;
    }
}

class BodyDemo implements Cloneable{

    @Override
    protected Object clone() throws CloneNotSupportedException {//重写clone方法
        return super.clone();	
    }
}


执行程序:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7QCV4Lu-1613718350093)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219141129041.png)]

这样我们就是先了深拷贝了,也就是复制了一个全新的personDemo2对象了。内部的引用类型Body也是一个新对象,而不是简单的引用复制了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxNOUFPv-1613718350093)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219142632675.png)]

​ 这里在多问一句,如果成员变量BodyDemo中还有其他引用类型作为成员变量时,这个时候如何实现深拷贝。答案还是相同的:需要将BodyDemo对象中的引用类型也实现Cloneable接口,同时重写clone方法。

​ 这个过程就像套娃一样,只要某个对象的成员变量是引用类型,要实现完全的深拷贝,就要将每一层依赖的引用类型都要事先实现Cloneable接口,同时重写clone方法。

例如:

package test.java.lang;

/**
 * 分析Object类的clone方法
 */
public class ObjectCloneDemo2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        HeadDemo headDemo = new HeadDemo();
        BodyDemo body = new BodyDemo(headDemo);
        PersonDemo2 p1 = new PersonDemo2(body, 18);
        PersonDemo2 p2 = (PersonDemo2) p1.clone();
        System.out.println(p1 == p2);
        System.out.println(p1.getBody() == p2.getBody());
        System.out.println(p1.getBody().getHeadDemo() == p2.getBody().getHeadDemo());
    }
}

class PersonDemo2 implements Cloneable {

    private BodyDemo body;

    private int age;

    public PersonDemo2() {
    }

    public PersonDemo2(BodyDemo body, int age) {
        this.body = body;
        this.age = age;
    }

    public BodyDemo getBody() {
        return body;
    }

    public void setBody(BodyDemo body) {
        this.body = body;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        PersonDemo2 personDemo2 = (PersonDemo2) super.clone();
        personDemo2.body = (BodyDemo) body.clone();
        return personDemo2;
    }
}

class BodyDemo implements Cloneable{

    private HeadDemo headDemo;

    public BodyDemo() {
    }

    public BodyDemo(HeadDemo headDemo) {
        this.headDemo = headDemo;
    }

    public HeadDemo getHeadDemo() {
        return headDemo;
    }

    public void setHeadDemo(HeadDemo headDemo) {
        this.headDemo = headDemo;
    }

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

class HeadDemo{

}


执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7mADmBU-1613718350094)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219142425601.png)]

BodyDemo中依赖HeadDemo,但是HeadDemo没有实现Cloneable,和重现clone方法,那么clone时就不是深拷贝了,在HeadDemo这一层复制时,就是引用复制,并没有赋值新的HeadDemo对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZtJ9nSA-1613718350096)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219142548462.png)]

要想实现完全深拷贝,就要按上面所说,HeadDemo也要实现实现Cloneable,和重现clone方法。

package test.java.lang;

/**
 * 分析Object类的clone方法
 */
public class ObjectCloneDemo2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        HeadDemo headDemo = new HeadDemo();
        BodyDemo body = new BodyDemo(headDemo);
        PersonDemo2 p1 = new PersonDemo2(body, 18);
        PersonDemo2 p2 = (PersonDemo2) p1.clone();
        System.out.println(p1 == p2);
        System.out.println(p1.getBody() == p2.getBody());
        System.out.println(p1.getBody().getHeadDemo() == p2.getBody().getHeadDemo());
    }
}

class PersonDemo2 implements Cloneable {

    private BodyDemo body;

    private int age;

    public PersonDemo2() {
    }

    public PersonDemo2(BodyDemo body, int age) {
        this.body = body;
        this.age = age;
    }

    public BodyDemo getBody() {
        return body;
    }

    public void setBody(BodyDemo body) {
        this.body = body;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        PersonDemo2 personDemo2 = (PersonDemo2) super.clone();
        personDemo2.body = (BodyDemo) body.clone();
        return personDemo2;
    }
}

class BodyDemo implements Cloneable{

    private HeadDemo headDemo;

    public BodyDemo() {
    }

    public BodyDemo(HeadDemo headDemo) {
        this.headDemo = headDemo;
    }

    public HeadDemo getHeadDemo() {
        return headDemo;
    }

    public void setHeadDemo(HeadDemo headDemo) {
        this.headDemo = headDemo;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        BodyDemo bodyDemo = (BodyDemo) super.clone();
        bodyDemo.headDemo = (HeadDemo) this.headDemo.clone();
        return bodyDemo;
    }
}

class HeadDemo implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CX9nX8Nr-1613718350098)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219144324017.png)]

这样就实现了真正的深拷贝了。内存分布如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYXjOMhg-1613718350099)(C:\Users\viruser.v-desktop\AppData\Roaming\Typora\typora-user-images\image-20210219144431856.png)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值