返回介绍

12.2.4 成功的克隆

发布于 2024-10-15 23:56:26 字数 2567 浏览 0 评论 0 收藏 0

理解了实现 clone() 方法背后的所有细节后,便可创建出能方便复制的类,以便提供了一个本地副本:

//: LocalCopy.java
// Creating local copies with clone()
import java.util.*;

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);
  }
}

public class LocalCopy {
  static MyObject g(MyObject v) {
    // Passing a handle, modifies outside object:
    v.i++;
    return v;
  }
  static MyObject f(MyObject v) {
    v = (MyObject)v.clone(); // Local copy
    v.i++;
    return v;
  }
  public static void main(String[] args) {
    MyObject a = new MyObject(11);
    MyObject b = g(a);
    // Testing handle equivalence,
    // not object equivalence:
    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);
  }
} ///:~

不管怎样,clone() 必须能够访问,所以必须将其设为 public(公共的)。其次,作为 clone() 的初期行动,应调用 clone() 的基础类版本。这里调用的 clone() 是 Object 内部预先定义好的。之所以能调用它,是由于它具有 protected(受到保护的)属性,所以能在衍生的类里访问。

Object.clone() 会检查原先的对象有多大,再为新对象腾出足够多的内存,将所有二进制位从原来的对象复制到新对象。这叫作“按位复制”,而且按一般的想法,这个工作应该是由 clone() 方法来做的。但在 Object.clone() 正式开始操作前,首先会检查一个类是否 Cloneable,即是否具有克隆能力——换言之,它是否实现了 Cloneable 接口。若未实现,Object.clone() 就掷出一个 CloneNotSupportedException 违例,指出我们不能克隆它。因此,我们最好用一个 try-catch 块将对 super.clone() 的调用代码包围(或封装)起来,试图捕获一个应当永不出现的违例(因为这里确实已实现了 Cloneable 接口)。

在 LocalCopy 中,两个方法 g() 和 f() 揭示出两种参数传递方法间的差异。其中,g() 演示的是按引用传递,它会修改外部对象,并返回对那个外部对象的一个引用。而 f() 是对自变量进行克隆,所以将其分离出来,并让原来的对象保持独立。随后,它继续做它希望的事情。甚至能返回指向这个新对象的一个句柄,而且不会对原来的对象产生任何副作用。注意下面这个多少有些古怪的语句:

v = (MyObject)v.clone();

它的作用正是创建一个本地副本。为避免被这样的一个语句搞混淆,记住这种相当奇怪的编码形式在 Java 中是完全允许的,因为有一个名字的所有东西实际都是一个句柄。所以句柄 v 用于克隆一个它所指向的副本,而且最终返回指向基础类型 Object 的一个句柄(因为它在 Object.clone() 中是那样被定义的),随后必须将其造型为正确的类型。

在 main() 中,两种不同参数传递方式的区别在于它们分别测试了一个不同的方法。输出结果如下:

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

大家要记住这样一个事实:Java 对“是否等价”的测试并不对所比较对象的内部进行检查,从而核实它们的值是否相同。==和!=运算符只是简单地对比句柄的内容。若句柄内的地址相同,就认为句柄指向同样的对象,所以认为它们是“等价”的。所以运算符真正检测的是“由于别名问题,句柄是否指向同一个对象?”

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文