- 写在前面的话
- 引言
- 第 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 推荐读物
11.1.1 Class 对象
为理解 RTTI 在 Java 里如何工作,首先必须了解类型信息在运行期是如何表示的。这时要用到一个名为“Class 对象”的特殊形式的对象,其中包含了与类有关的信息(有时也把它叫作“元类”)。事实上,我们要用 Class 对象创建属于某个类的全部“常规”或“普通”对象。
对于作为程序一部分的每个类,它们都有一个 Class 对象。换言之,每次写一个新类时,同时也会创建一个 Class 对象(更恰当地说,是保存在一个完全同名的.class 文件中)。在运行期,一旦我们想生成那个类的一个对象,用于执行程序的 Java 虚拟机(JVM)首先就会检查那个类型的 Class 对象是否已经载入。若尚未载入,JVM 就会查找同名的.class 文件,并将其载入。所以 Java 程序启动时并不是完全载入的,这一点与许多传统语言都不同。
一旦那个类型的 Class 对象进入内存,就用它创建那一类型的所有对象。
若这种说法多少让你产生了一点儿迷惑,或者并没有真正理解它,下面这个示范程序或许能提供进一步的帮助:
//: SweetShop.java // Examination of the way the class loader works class Candy { static { System.out.println("Loading Candy"); } } class Gum { static { System.out.println("Loading Gum"); } } class Cookie { static { System.out.println("Loading Cookie"); } } public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { e.printStackTrace(); } System.out.println( "After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); } } ///:~
对每个类来说(Candy,Gum 和 Cookie),它们都有一个 static 从句,用于在类首次载入时执行。相应的信息会打印出来,告诉我们载入是什么时候进行的。在 main() 中,对象的创建代码位于打印语句之间,以便侦测载入时间。
特别有趣的一行是:
Class.forName("Gum");
该方法是 Class(即全部 Class 所从属的)的一个 static 成员。而 Class 对象和其他任何对象都是类似的,所以能够获取和控制它的一个句柄(装载模块就是干这件事的)。为获得 Class 的一个句柄,一个办法是使用 forName()。它的作用是取得包含了目标类文本名字的一个 String(注意拼写和大小写)。最后返回的是一个 Class 句柄。
该程序在某个 JVM 中的输出如下:
inside main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie
可以看到,每个 Class 只有在它需要的时候才会载入,而 static 初始化工作是在类载入时执行的。
非常有趣的是,另一个 JVM 的输出变成了另一个样子:
Loading Candy Loading Cookie inside main After creating Candy Loading Gum After Class.forName("Gum") After creating Cookie
看来 JVM 通过检查 main() 中的代码,已经预测到了对 Candy 和 Cookie 的需要,但却看不到 Gum,因为它是通过对 forName() 的一个调用创建的,而不是通过更典型的 new 调用。尽管这个 JVM 也达到了我们希望的效果,因为确实会在我们需要之前载入那些类,但却不能肯定这儿展示的行为百分之百正确。
1. 类标记
在 Java 1.1 中,可以采用第二种方式来产生 Class 对象的句柄:使用“类标记”。对上述程序来说,看起来就象下面这样:
Gum.class;
这样做不仅更加简单,而且更安全,因为它会在编译期间得到检查。由于它取消了对方法调用的需要,所以执行的效率也会更高。
类标记不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。除此以外,针对每种基本数据类型的封装器类,它还存在一个名为 TYPE 的标准字段。TYPE 字段的作用是为相关的基本数据类型产生 Class 对象的一个句柄,如下所示:
……等价于……
... is equivalent to ... | |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论