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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值