您的位置:首页 > 编程语言 > Java开发

Java类型检查 学习笔记

2010-11-04 10:15 323 查看
运行期类型识别(RTTI,run-time type identification)的概念初看起来非常简单:当你只有一个指向对象的基类的引用时,RTTI 机制可以让你找出这个对象确切的类型。

Java 是如何允许我们在运行期识别对象和类的信息。主要有两种方式:一种是传统的RTTI,它假定我们在编译期和运行期已经知道了所有的类型;另一种是“反射机制(reflection)”,它允许我们在运行期获得类的信息。

在Java 中,所有的类型转换都是在运行期检查的。这也是RTTI 名字的来源:在运行期间,识别一个对象的类型。

要理解 RTTI 在Java 中是如何工作的,首先必须要知道类型信息在运行期是如何表示的。这项工作是由被称为“Class 对象”的特殊对象完成的,它包含了与类有关的信息。事实上,Class 对象正是被用来创建类的“常规”对象的。

作为程序一部分,每个类都有一个 Class 对象。换言之,每当你编写并且编译了一个新类,就会产生一个Class 对象(更恰当地说,是被保存在一个同名的.class 文件中)。在运行期,一旦我们想生成这个类的一个对象,运行这个程序的Java 虚拟机(JVM)首先检查这个类的Class 对象是否已经加载。如果尚未加载,JVM 就会根据类名查找.class 文件,并将其载入。所以Java 程序并不是一开始执行,就被完全加载的,这一点与许多传统语言都不同。

Class 对象仅在需要的时候才被加载,static 语句块是在类加载时被执行的。

Class.forName("Gum");这是 Class 类(所有Class 对象都属于这个类型)的一个static 成员。forName()是取得Class 对象的引用的一种方法。它是用一个包含目标类的文本名(注意拼写和大小写)的String 作输入参数,返回的是一个Class 对象的引用,上面的代码忽略了返回值。对forName()的调用是为了它产生的“副作用”:如果类Gum 还没有被加载就加载它。在加载的过程中,Gum 的static 语句被执行。

类字面常量(Class literal)

Java 还提供了另一种方法来生成Class 对象的引用:使用“类字面常量(classliteral)”。对上述程序来说,看起来就象下面这样:Gum.class;这样做不仅更简单,而且更安全,因为它在编译期就会受到检查。并且它无需方法调用,所以也更高效。

迄今为止,我们已知的 RTTI 形式包括:

1. 经典的类型转换,如"(Shape)",由RTTI 确保类型转换的正确性,如果你执行了一个错误的类型转换,就会抛出一个ClassCastException 异常。

2. 代表对象类型的 Class 对象。通过查询Class 对象可以获取运行期所需的信息。

3.RTTI 在Java 中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象

是不是某个特定类型的实例

用Class.forName()创建了一个名为petTypes 的Class 对象的数组。从petTyeps 中随机选择Class 对象, 并通过Class.newInstance()调用缺省的(无参数)构造器生成新的对象,加入到pets 数组中。

对instanceof 有比较严格的限制:你只可将其与类型的名字进行比较,而不能与Class对象作比较。

动态的instanceof

Class.isInstance 方法提供了一种动态地调用instanceof 运算符的途径。petTypes[j].isInstance(o)

等价性: instanceof vs. Class

在查询类型信息时,以instanceof 的形式(或者是以isInstance()的形式,它们产生相同的结果)与直接比较Class 对象有一个很重要的差别。

instancof 和isInstance()生成的结果完全一样,equals()和==也一样。但是这两组测试得出的结论却不相同。instanceof 保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而另一种情况是,如果你用==比较实际的Class 对象,就不包含继承关系,——它或者恰好是这个确切的类型,或者不是。

RTTI 语法

首先, 你需要获得指向适当的Class 对象的引用。一种办法是用字符串以及Class.forName()方法,就象前例演示的那样。这种做法很方便,因为你在获取Class的引用事,并不需要生成该Class 类型的对象。然而,如果你已经有了一个你感兴趣的类型的对象,那么你就可以通过调用getClass()来获取Class 的引用,这是根类Object提供的方法。它返回Class 的引用,用来表示对象的实际类型。

Class.getInterfaces()方法返回Class 对象的数组,这些对象代表的是某个Class对象所包含的接口。Class[] faces = c.getInterfaces(); Class cy = c.getSuperclass();Object o = o = cy.newInstance();使用newInstance()的类必须要有一个缺省构造器。在下一节,通过使用Java 反射API(Java reflecion API),你会看到如何使用任意的构造器来动态地创建对象。

反射(Reflection):运行期的类信息

如果你不知道某个对象的确切类型,RTTI 可以告诉你。但是有一个限制:这个类型在编译期必须已知,才能使用RTTI 识别它,并利用这些信息做一些有用的事。换句话说,在编译期,编译器必须知道你要通过RTTI 来处理的所有类。

Class 类(本章前面已有论述)支持反射的概念,Java 附带的库java.lang.reflect包含了Field,Method 以及Constructor 类(每个类都实现了Member 接口)。这些类型的对象是由JVM 在运行期创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor 创建新的对象,用get()和set()方法读取和修改与Field 对象关联的属

性,用invoke()方法调用与Method对象关联的方法。

RTTI 和反射之间真正的区别只在于,对RTTI 来说,编译器在编译期打开和检查.class 文件。(换句话说,我们可以用“普通”方式调用一个对象的所有方法。)而对于反射机制来说.class 文件在编译期间是不可获取的,所以是在运行期打开和检查.class 文件。

类方法提取器

你很少直接使用反射机制;它在Java中是用来支持其他特性的,例如第十二章的对象序列化(serialization),第十四章的JavaBean。但是有的时候如果能动态地提取某个类的信息还是很有用的。类方法提取器就是一个非常有用的工具。

Class 的getMethods()和getConstructors()方法分别返回Method 数组和Constructor 数组。这两个类都提供了深层方法,用以解析其对象所代表的方法,并获取其名字、输入参数以及返回值。但你也可以象这里一样,只使用toString()生成一个含有完整方法签名的字符串
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: