Java中为什么需要克隆方法

文章讨论了Java中的对象克隆机制,解释了为何需要`clone`方法以及其目标。`clone`方法用于创建对象的副本,但默认实现可能产生浅拷贝而非深拷贝。文中通过一个`Computer`和`Cpu`类的例子展示了当内部对象需要被克隆时,需要重写`clone`方法以实现深拷贝。如果不进行处理,克隆后的对象可能会共享原来的对象引用,导致意外的修改同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java克隆详解

首先我们来想一下为什么需要clone 这个方法?
没有clone,其实我们也是可以自己new 对象来实现,不过如果这个对象中有很多属性,并且很多都已经赋值,我们此时想要一个和这个对象一模一样的对象该怎么搞?
手动提取值,然后一个一个set 进去么?太麻烦了,我们如果能实现一个方法自动帮我们来复制其中的属性他不香么?

在这里插入图片描述
所以Object 类已经自带了clone 方法,来帮助我们实现对象的克隆。
在这里插入图片描述

在这个方法注释我们可以看到他设计这个clone 所需要达成的目标
第一个说明 克隆出来的对象应该有单独的内存地址分配,和原对象不是同一个。
第二个说明,原始和克隆的对象应该属于相同的类类型
第三个说明,原始和克隆的对象应该是相等的
二三两个说明并不是强制的。
下面给出 源码注释的翻译,关键部分我重点画出来了。

按照惯例,返回的对象应该通过调用 super.clone 来获取。如果一个类及其所有超类(Object 除外)都遵循此约定,则 x.clone().getClass() == x.getClass()。
按照约定,此方法返回的对象应独立于此对象(正在克隆的对象)。为了实现这种独立性,可能需要在返回之前修改 super.clone 返回的对象中的一个或多个字段。通常,这意味着复制构成被克隆对象的内部“深层结构”的任何可变对象,并将对这些对象的引用替换为对副本的引用。如果一个类只包含基元字段或对不可变对象的引用,那么通常不需要修改 super.clone 返回的对象中的任何字段。
类对象的方法克隆执行特定的克隆操作。首先,如果此对象的类未实现接口 Cloneable,则会抛CloneNotSupportedException。请注意,所有数组都被视为实现接口 Cloneable,并且数组类型 T[] 的克隆方法的返回类型为 T[],其中 T 是任何引用或基元类型。否则,此方法将创建此对象的类的新实例,并使用该对象的相应字段的内容准确初始化其所有字段,就像通过赋值一样;字段的内容本身不会克隆。因此,此方法执行此对象的“浅拷贝”,而不是“深拷贝”操作。
类 Object 本身不实现接口 Cloneable,因此在类为 Object 的对象上调用克隆方法将导致在运行时引发异常。

这段话的意思我简要说下:
我们的目的是:方法返回的对象应独立于此对象
为了实现这个目的,加入这个对象全是基本数据类型,那没事直接克隆就完事了;但是如果这个对象类型中还定义了其他对象,如果我们没有对其进行处理的话,新克隆出来对象其中的对象引用和原来对象指的是同一个,这就和我们的初衷不符合了。
所以我们重写clone 方法时也要对其内部的对象进行克隆。

public class testClone {

    public static void main(String[] args) {
        Cpu cpu = new Cpu("酷睿");
        Computer computer = new Computer();

        computer.setCpu(cpu);

        try {
            Computer clone = (Computer) computer.clone();

            System.out.println("克隆出来的CPU 是"+clone.getCpu().getName());
            System.out.println("现在修改原来对象的cpu为锐龙");
            cpu.setName("锐龙");
            System.out.println("克隆出来的CPU 是"+clone.getCpu().getName());

        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

现在我们有一个Computer 类,里面还包含了一个Cpu 类,我们在clone 方法中注释掉了 对内部对象 cpu 的重新克隆

public class Computer implements Cloneable{
    private Cpu cpu;

    public Cpu getCpu() {
        return cpu;
    }

    public void setCpu(Cpu cpu) {
        this.cpu = cpu;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Computer com;
        com= (Computer) super.clone();

       // com.setCpu(((Cpu) this.cpu));

        return com;
    }
}
public class Cpu implements Cloneable{
    public Cpu(String name) {
        this.name = name;
    }

    private String name;

    public Cpu() {
    }

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

    public String getName() {
        return name;
    }

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

执行结果为
在这里插入图片描述
我们明明修改的是原对象的CPU,拷贝对象的CPU也变了。

让我们现在取消注释在这里插入图片描述
执行结果为
在这里插入图片描述
克隆出来对象的CPU还是酷睿。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值