Object克隆方法解析

克隆方法的由来

问题一:什么是克隆(clone)方法

答: 创建并返回此对象的一个副本——按照原对象,创建一个新的对象(复制原对象的内容)。

问题二:已经存在new关键字反射技术都可以创建对象,为什么还需要一个Object的clone方法呢?

答:必然是new关键字和反射技术,存在一些弊端。

new关键字和反射创建对象的弊端:
通过new和反射可以创建内容一模一样的对象。但是,创建对象之后,通过setter方法,完成设置(不一样的内容),如果需要创建更多的内容一致的对象,那么setter方法调用就不断在重复。

使用clone方法创建对象

Object的clone方法使用步骤:

  1. 在需要调用clone方法的对象上添加实现Cloneable接口;
  2. 复写clone方法,在自己的clone方法中调用父类的clone方法,将返回值类型强转成本类类型,将当前clone方法修饰符改成public;
  3. 调用对象的clone方法;

代码演示:

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

	//....省略get/set/toString方法
	
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(18);

        Person p2 = p1.clone();

        System.out.println(p1+":"+p1.hashCode());
        System.out.println(p2+":"+p2.hashCode());
    }
}

//控制台打印结果:
Person{name='张三', age=18}:1908153060
Person{name='张三', age=18}:116211441

通过使用clone方法,我们发现大大的减少了创建重复对象代码。这也就是clone方法存在的意义。

克隆出来的对象和原来的对象有什么关系:
通过上面的测试,我们已经知道了,克隆出来的对象内容一致,但是对象哈希值不同,所以是不同对象。

那么两个对象的内容之间有什么关联呢——两个对象的内容是彼此独立,还是,两个对象底层使用的同一个内容呢?
代码演示:

public class Person implements Cloneable{
    private String name;
    private Integer age;
    private Children child;

	//....省略get/set/toString方法

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Children {
    private String name;
    private Integer age;

	//....省略get/set/toString方法
}
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(28);

        Children children1 = new Children();
        children1.setName("张伟");
        children1.setAge(5);
        p1.setChild(children1);

        Person p2 = p1.clone();

        System.out.println(p1+":对象的哈希值:"+p1.hashCode()+":child成员变量的哈希值:"+p1.getChild().hashCode());
        System.out.println(p2+":对象的哈希值:"+p2.hashCode()+":child成员变量的哈希值:"+p2.getChild().hashCode());
    }
}

//控制台打印结果
Person{name='张三', age=28, child=Children{name='张伟', age=5}}:对象的哈希值:116211441:child成员变量的哈希值:607635164
Person{name='张三', age=28, child=Children{name='张伟', age=5}}:对象的哈希值:529116035:child成员变量的哈希值:607635164

结论:通过测试发现克隆出来的对象虽然不一致,但是底层的成员变量的哈希值是一致的。

这种复制我们称之为:浅表复制

浅表复制的内存结构:
在这里插入图片描述
浅表复制的弊端:
由于浅表复制导致克隆的对象中成员变量的底层哈希值一致,如果我们操作其中一个对象的成员变量内容,就会导致,所有的克隆对象的成员内容发送改变。

结论:clone方法默认的复制操作是浅表复制,浅表复制存在弊端——仅仅创建新的对象,对象的成员内容底层哈希值是一致的,因此,不管是原对象还是克隆对象,只有其中一个修改了成员的数据,就会影响所有的原对象和克隆对象。

要解决浅表复制的问题:进行深层的复制

深层复制

目的:不仅在执行克隆的时候,克隆对象是一个新对象,而且,克隆对象中的成员变量,也要求是一个新的对象
开发步骤:

  1. 修改children类实现Cloneable接口;
  2. 修改children类重写clone方法;
  3. 修改Person类重写clone方法,在clone方法中调用children的clone方法;

代码演示:

public class Children implements Cloneable {
    private String name;
    private Integer age;

	//....省略get/set/toString方法

    @Override
    public Children clone() throws CloneNotSupportedException {
        return (Children) super.clone();
    }
}
public class Person implements Cloneable{
    private String name;
    private Integer age;
    private Children child;

	//....省略get/set/toString方法

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person clone = (Person) super.clone();
        clone.setChild(child.clone());
        return clone;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(28);

        Children children1 = new Children();
        children1.setName("张伟");
        children1.setAge(5);
        p1.setChild(children1);

        Person p2 = p1.clone();

        System.out.println(p1.getChild());
        System.out.println(p2.getChild());

        children1.setName("张三丰");
        System.out.println(p1.getChild());
        System.out.println(p2.getChild());

        Children children2 = p2.getChild();
        children2.setName("张无忌");
        System.out.println(p1.getChild());
        System.out.println(p2.getChild());

        System.out.println(p1.getChild().hashCode());
        System.out.println(p2.getChild().hashCode());
    }
}
//控制台打印结果
Children{name='张伟', age=5}
Children{name='张伟', age=5}
Children{name='张三丰', age=5}
Children{name='张伟', age=5}
Children{name='张三丰', age=5}
Children{name='张无忌', age=5}
116211441
607635164

可以看到,深层复制成员变量如果是对象,那么也会创建新的对象。

使用clone接口实现深层复制的弊端:
虽然深度复制成员变量对象也是创建新的对象,但是修改类中成员变量对应的源码,如果成员变量特别多,那么就需要修改多个类的源码;
比如:前面的Person类中现在又新增了Son、Sister、Mother啥全家福的,那对应需要修改的类就特别多了;

深层复制的几种方法

原文:Java深度克隆的4种方法

以上为学习所做笔记,来源为黑马程序员Object课程!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值