Java 中的 clone 方法

本文通过示例讲解了Java中如何使用clone方法创建对象的副本,区分浅复制与深复制的区别,并展示了如何实现深复制以确保复制的对象独立于原始对象。

首先,我们看一个示例:

class Obj {
    private int aInt = 0;

    public int getaInt() {
        return aInt;
    }

    public void setaInt(int aInt) {
        this.aInt = aInt;
    }

}

public class TestRef {
    public static void main(String[] args) {
        Obj a = new Obj();
        Obj b = a;
        b.setaInt(5);
        System.out.println("a: " + a.getaInt());
        System.out.println("b: " + b.getaInt());
    }
}

输出结果:a: 5
b: 5

我们发现,对象 b 的改变影响了对象 a 的状态,但是,实际编程中,往往希望从某个已知的对象 A 创建出一个与 A 具有相同状态的对象 B, 并且对 B 的修改不会影响 A 的状态。怎么办呢?
幸运的是,Java 提供了一种简单有效的 clone 方法来满足此需求。
我们都知道,Java 中所有类默认都继承自 Object 类,而 Object类中提供了 clone()方法,作用是返回一个 Object 对象的复制,这个复制函数返回的是一个新的对象而不是一个引用。以下是 clone() 方法的步骤。
1. 实现 clone 的类首先需要继承 Cloneable 接口。
2. 重写 Object 类中的 clone() 方法
3. clone() 方法中调用 super.clone()
4. 把浅复制的引用指向原型对象新的克隆体

class Obj implements Cloneable{
    private int aInt = 0;

    public int getaInt() {
        return aInt;
    }

    public void setaInt(int aInt) {
        this.aInt = aInt;
    }

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

}

public class TestRef {
    public static void main(String[] args) throws CloneNotSupportedException {
        Obj a = new Obj();
        Obj b = (Obj) a.clone();
        b.setaInt(5);
        System.out.println("a: " + a.getaInt());
        System.out.println("b: " + b.getaInt());
    }
}

输出结果:a: 0
b: 5

clone() 方法的调用完美的解决了上述问题,但是细心的我们会发现上文步骤 4 中提到了浅复制,那么什么是浅复制?是不是还有深复制?
浅复制:复制对象(如下例中的p2)的所有变量含有与被复制对象(p1)相同的值,而且对象的引用指向同一个地方
深复制:复制对象(如下例中的p2)的所有变量含有与被复制对象(p1)相同的值,但是对象的引用指向不同的地方
下面,我们用示例看一下他们之间的区别:

class Birthday implements Cloneable {
    public int month;
    public int day;

    public Birthday(int month, int day) {
        super();
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return month + "/" + day;
    }

    @Override
    protected Object clone() {
        Birthday b = null;
        try {
            b = (Birthday) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return b;
    }



}

class People implements Cloneable {
    public String name;
    public Birthday bir;

    public People(String name, Birthday bir) {
        super();
        this.name = name;
        this.bir = bir;
    }

    @Override
    protected Object clone() {
        People p = null;
        try {
            p =  (People) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

}

public class TestClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        People p1 = new People("小明", new Birthday(1, 1));
        People p2 = (People) p1.clone();
        p2.name = "小红";
        p2.bir.month = 2;
        p2.bir.day = 2;
        System.out.println("p1的姓名:" + p1.name + " ------ p1的生日:" + p1.bir);
        System.out.println("p2的姓名:" + p2.name + " ------ p2的生日:" + p2.bir);
    }
}

输出:p1的姓名:小明 —— p1的生日:2/2
p2的姓名:小红 —— p2的生日:2/2

我们发现,随着对象 p2 的改变,对象 p1 的引用属性(Birthday对象)也发生改变,这不是我们所希望的。而这就是我们所说的浅复制,深复制再看下一个示例:

class Birthday implements Cloneable {
    public int month;
    public int day;

    public Birthday(int month, int day) {
        super();
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return month + "/" + day;
    }

    @Override
    protected Object clone() {
        Birthday b = null;
        try {
            b = (Birthday) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return b;
    }



}

class People implements Cloneable {
    public String name;
    public Birthday bir;

    public People(String name, Birthday bir) {
        super();
        this.name = name;
        this.bir = bir;
    }

    @Override
    protected Object clone() {
        People p = null;
        try {
            p =  (People) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        //实现深复制
        p.bir = (Birthday) this.bir.clone();
        return p;
    }

}

public class TestClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        People p1 = new People("小明", new Birthday(1, 1));
        People p2 = (People) p1.clone();
        p2.name = "小红";
        p2.bir.month = 2;
        p2.bir.day = 2;
        System.out.println("p1的姓名:" + p1.name + " ------ p1的生日:" + p1.bir);
        System.out.println("p2的姓名:" + p2.name + " ------ p2的生日:" + p2.bir);
    }
}

输出:p1的姓名:小明 —— p1的生日:1/1
p2的姓名:小红 —— p2的生日:2/2

我们再用一个图行来解释其区别
这里写图片描述

Java中的clone()方法Object类中定义的方法,它用于创建并返回当前对象的一个副本。这个副本就是一个新的对象,它与原始对象具有相同的属性方法。通常情况下,我们需要在一个类中实现clone()方法来支持对象的克隆。 克隆对象是在Java中处理对象的一种常见方式。通过克隆,我们可以在不修改原始对象的情况下创建该对象的一个副本。这在某些情况下是非常有用的,例如在多线程环境下,我们需要多个线程同时访问同一个对象,但是又不希望它们之间相互干扰。 在Java中,要使用clone()方法来实现对象的克隆,我们需要满足两个条件: 1. 实现Cloneable接口:这个接口是一个标记接口,它没有任何方法,只是用来标记一个类可以被克隆。 2. 重写clone()方法:这个方法Object类中的一个protected方法,需要在我们的类中进行重写。在重写这个方法时,我们需要调用super.clone()方法来创建一个新的对象,并将原始对象中的属性复制到这个新对象中。 下面是一个示例代码,演示了如何在Java中实现对象的克隆: ``` public class MyClass implements Cloneable { private int value; public MyClass(int value) { this.value = value; } public int getValue() { return value; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } ``` 在这个示例代码中,我们实现了一个MyClass类,并重写了clone()方法。在这个方法中,我们调用了super.clone()方法来创建一个新的对象,并返回这个新对象。由于我们的类实现了Cloneable接口,因此它可以被克隆。 使用这个类进行克隆的代码如下: ``` MyClass obj1 = new MyClass(10); MyClass obj2 = (MyClass) obj1.clone(); System.out.println(obj1.getValue()); // 输出10 System.out.println(obj2.getValue()); // 输出10 ``` 在这个代码中,我们创建了一个MyClass对象obj1,并将其克隆为obj2。由于这两个对象具有相同的属性方法,因此它们的输出结果也是相同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值