2018-05-28 对象clone()方法

2018-05-28  本文已影响0人  MiaLing007

java 的基类Object中定义了clone()(克隆)方法,但该方法并不是每个类都能调用,虽然该方法在Object中定义为protected作用域。
Object中方法:

    protected native Object clone() throws CloneNotSupportedException;

想要使用clone()方法,必须Cloneable接口,且重写clone()方法。

例如:


class MyObject implements Cloneable {
    int i;
    MyObject(int ii) {i = ii;}
    protected 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);
    }
}

public class LocalCopy {
    static MyObject g(MyObject v) {
        v.i++;
        return v;
    }
    static MyObject f(MyObject v) {
        v = (MyObject)v.clone();
        v.i++;
        return v;
    }
    public static void main(String[] args) {
        MyObject a = new MyObject(11);
        MyObject b = g(a);
        if (a == b)
            System.out.println("a == b");
        else
            System.out.println("a != b");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        
        MyObject c = new MyObject(47);
        MyObject d = f(c);
        if (c == d)
            System.out.println("c == d");
        else
            System.out.println("c != d");
        System.out.println("c = " + c);
        System.out.println("d = " + d);
    }
}

输出结果为:

a == b
a = 12
b = 12
c != d
c = 47
d = 48

在LocalCopy 中,两个方法g()和f()揭示出两种参数传递方法间的差异。其中,g()演示的是按引用传递,
它会修改外部对象,并返回对那个外部对象的一个引用。而f()是对自变量进行克隆,所以将其分离出来,
并让原来的对象保持独立。随后,它继续做它希望的事情。甚至能返回指向这个新对象的一个句柄,而且不
会对原来的对象产生任何副作用。注意下面这个多少有些古怪的语句:
v = (MyObject)v.clone();
它的作用正是创建一个本地副本。为避免被这样的一个语句搞混淆,记住这种相当奇怪的编码形式在Java 中
是完全允许的,因为有一个名字的所有东西实际都是一个句柄。所以句柄v 用于克隆一个它所指向的副本,
而且最终返回指向基础类型Object 的一个句柄(因为它在Object.clone()中是那样被定义的),随后必须
将其造型为正确的类型。

clone()该方法为浅克隆,浅层的复制

例如:

public class Snake implements Cloneable{
    private Snake next;
    private char c;
    
    Snake(int i, char x) {
        c = x;
        if (--i > 0)
            next = new Snake(i, (char)(x + 1));
    }
    void increment() {
        c++;
        if (next != null)
            next.increment();
    }
    public String toString() {
        String s = ":" + c;
        if (next != null)
            s +=next.toString();
        return s;
    }
    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
    public static void main(String args[]) {
        Snake s = new Snake(5, 'a');
        System.out.println("s = " + s);
        Snake s2 = (Snake)s.clone();
        System.out.println("s2 = " + s2);
        s.increment();
        System.out.println("after s.increment, s2 = " + s2);
    }
}

一条Snake(蛇)由数段构成,每一段的类型都是Snake。所以,这是一个一段段链接起来的列表。所有段都
是以循环方式创建的,每做好一段,都会使第一个构建器参数的值递减,直至最终为零。而为给每段赋予一
个独一无二的标记,第二个参数(一个Char)的值在每次循环构建器调用时都会递增。
increment()方法的作用是循环递增每个标记,使我们能看到发生的变化;而toString 则循环打印出每个标
记。输出如下:

s = :a:b:c:d:e
s2 = :a:b:c:d:e
after s.increment, s2 = :a:c:d:e:f

这意味着只有第一段才是由Object.clone()复制的,所以此时进行的是一种“浅层复制”。若希望复制整条
蛇——即进行“深层复制”——必须在被覆盖的clone()里采取附加的操作。
通常可在从一个能克隆的类里调用super.clone(),以确保所有基础类行动(包括Object.clone())能够进
行。随着是为对象内每个句柄都明确调用一个clone();否则那些句柄会别名变成原始对象的句柄。构建器
的调用也大致相同——首先构造基础类,然后是下一个衍生的构建器⋯⋯以此类推,直到位于最深层的衍生
构建器。区别在于clone()并不是个构建器,所以没有办法实现自动克隆。为了克隆,必须由自己明确进
行。

上一篇 下一篇

猜你喜欢

热点阅读