Java--对象的克隆

因为Java方法中对象参数的传递是句柄(引用)传递,所以方法中对句柄的改动会影响到原对象。

很少调用一个方法来处理它的参数,在实际编程中也要避免改变参数的情况。但如果我们需要在一个方法的调用期间修改一个参数,且不打算修改外部实体,就应该在自己方法的内部制作一个那个参数的副本来保护那个参数。

制作对象副本需要使用clone()方法。这个方法在基础类Object 中定义成“protected”模式。所以在希望克隆的任何衍生类中,必须将其覆盖为“public”模式。例如,标准库类Vector 覆盖了 clone(),所以能为 Vector调用clone()。

class Int {
    private int i;
    public Int(int ii) { i = ii; }
    public void increment() { i++; }
    public String toString() {
        return Integer.toString(i);
    }
}
public class Cloning {
    public static void main(String[] args) {
        Vector v = new Vector();    //创建Vector
        for(int i = 0; i < 10; i++ )
            v.addElement(new Int(i));    //添加对象
        System.out.println("v: " + v);    //第一次输出  
        Vector v2 = (Vector)v.clone();    //创建Vector的复制
        for(Enumeration e = v2.elements();   
            e.hasMoreElements(); )    //对复制的Vector中的对象进行+1操作
        ((Int)e.nextElement()).increment();
        System.out.println("v: " + v);     //第二次输出
    }
} 

上面代码涉及到“深拷贝“和”浅拷贝“的问题。这个例子中由于别名问题,原Vector和拷贝的Vector都包含了相同的对象,即两个Vector是完全独立的,但其中容纳的东西相同,这就是”浅拷贝“。”浅拷贝“只拷贝本对象,该对象内部指向的其他对象,以及”其他对象“又指向的另外的对象都不拷贝。

若要“深拷贝”,必须在覆盖的clone()里采取附加的操作。 通常在调用 super.clone()后,为对象内每个句柄都明确调用一个 clone();否则那些句柄会别名变成原始对象的句柄。当然,必须保证对象内的每个句柄自身也都实现了“深拷贝”。

使类具有克隆能力:

因为Object类将clone()定义为”protected", 所以衍生类不做点什么的话,是无法具有克隆能力的(很简单,对象无法调用本类的protected方法)。

1、覆盖clone()方法为public. 

想要类可以克隆,记住两点:

  1. 几乎肯定要调用super.clone();
  2. 将clone()设置为public。

注意:因为Java不可能在衍生之后反而缩小方法的访问范围,所以一旦对象变得可以克隆,从它衍生的任何东西都是可以克隆的。(下面会有一种特殊机制关闭克隆能力)

2、实现Cloneable接口

Cloneable接口是空的,我们不需要为它实现任何方法。它的存在有两个原因:

  • 可能有一个上溯造型句柄指向一个基础类型,而且不知道它是否真的能克隆那个对象。在这种情况下,可用instanceof 关键字调查句柄是否确实同一个能克隆的对象连接。
  • 考虑到我们可能不愿所有对象类型都能克隆。所以Object.clone()会验证一个类是否真的是实现了Cloneable 接口。若答案是否定的,则“掷”出一个 CloneNotSupportedException违例。

所以在一般情况下,我们必须将“implement Cloneable”作为对克隆能力提供支持的一部分。

经过上面两步,就可以创建自己的可克隆的类:

class MyObject implements Cloneable {
    int i;
    MyObject(int ii) { i = ii; }
    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("MyObject can't clone");
        }
        return o;
    }
    public String toString() { return Integer.toString(i); }
}

 消除克隆能力:

  1. 不实现 Cloneable 来试着防止克隆,并覆盖clone(),以产生一个违例。这样,从它衍生出来的类的clone()方法调用super.clone()时会抛出异常。
  2. 将类设为final,从而防止克隆。若clone()尚未被我们的任何一个上级类覆盖,这一设想便不会成功。 若已被覆盖,那么再一次覆盖它,并“掷”出一个 CloneNotSupportedException(克隆不支持)违例。为担保克隆被禁止,将类设为final是唯一的办法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏喵了个咪的博客空间

zephir-(5)类型

#zephir-类型# ? ##前言## 先在这里感谢各位zephir开源技术提供者 Zephir既可以使用动态类型也可以使用静态类型,这是zephir独特的一...

3709
来自专栏GreenLeaves

Javascript深拷贝

var oOriginal = { memNum: 1, // number ...

2036
来自专栏Jimoer

Java设计模式学习记录-原型模式

1465
来自专栏JMCui

读书笔记 之《Thinking in Java》(对象、集合、异常)

一、前言:     本来想看完书再整理下自己的笔记的,可是书才看了一半发现笔记有点多,有点乱,就先整理一份吧,顺便复习下前面的知识,之后的再补上。     真的...

3728
来自专栏Android开发指南

Effecvtive Java Note

3085
来自专栏深度学习自然语言处理

谈一谈python中的魔法变量*args和**kwargs

,没有注释,没有封装,没有可读性。哎,幸亏发现及时,现在正在写一个新的任务,刚好可以好好弄弄架构和代码了!

983
来自专栏猿人谷

浅谈C/C++中的指针和数组(一)

                                                       浅谈C/C++中的指针和数组(一)       指...

2185
来自专栏菜鸟计划

angularjs filter详解

过滤器(filter)正如其名,作用就是接收一个输入,通过某个规则进行处理,然后返回处理后的结果。 主要用在数据的格式化上,例如获取一个数组中的子集,对数组中的...

3738
来自专栏Java Web

Java 面试知识点解析(一)——基础知识篇

在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Jav...

4825
来自专栏张善友的专栏

检查Python对象

编程环境中的对象很象现实世界中的对象。实际的对象有一定的形状、大小、重量和其它特征。实际的对象还能够对其环境进行响应、与其它对象交互或执行任务。计算机中的对象试...

19610

扫码关注云+社区

领取腾讯云代金券