- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
12.2.4 成功的克隆
理解了实现 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论