think in java回顾整理之RTTI

运行期类型鉴定(RTTI)的概念初看非常简单——手上只有基础类型的一个句柄时,利用它判断一个对象的正确类型。

如何利用Java在运行期间查找对象和类信息:
一种是“传统”RTTI,它假定我们已在编译和运行期拥有所有类型;另一种是“反射”机制,利用它可在运行期独立查找类信息。

多形性是面向对象程序设计的一个常规目标。

类型信息在运行期是如何表示的?
这时要用到一个名为“Class对象”的特殊形式的对象,其中包含了与类有关的信息(有时也把它叫作“元类”)。
事实上,我们要用Class对象创建属于某个类的全部“常规”或“普通”对象。
对于作为程序一部分的每个类,它们都有一个Class对象。换言之,每次写一个新类时,同时也会创建一个Class对象(更恰当地说,是保存在一个完全同名的.class文件中)。在运行期,一旦我们想生成那个类的一个对象,用于执行程序的Java虚拟机(JVM)首先就会检查那个类型的Class对象是否已经载入。若尚未载入,JVM就会查找同名的.class文件,并将其载入。所以Java程序启动时并不是完全载入的,这一点与许多传统语言都不同。
一旦那个类型的Class对象进入内存,就用它创建那一类型的所有对象。

Class对象和其他任何对象都是类似的,所以能够获取和控制它的一个句柄(装载模块就是干这件事的)。
为获得Class的一个句柄,一个办法是使用forName()。它的作用是取得包含了目标类文本名字的一个String(注意拼写和大小写)。最后返回的是一个Class句柄。

每个Class只有在它需要的时候才会载入。

“类标记”:
类标记不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。
除此以外,针对每种基本数据类型的封装器类,它还存在一个名为TYPE的标准字段。TYPE字段的作用是为相关的基本数据类型产生Class对象的一个句柄。


RTTI形式包括:
(1) 经典造型,它用RTTI确保造型的正确性,并在遇到一个失败的造型后产生一个ClassCastException违例。
(2) 代表对象类型的Class对象。可查询Class对象,获取有用的运行期资料。
(3)关键字instanceof告诉我们对象是不是一个特定类型的实例(Instance即“实例”)

编译器能够自动上溯造型,编译器不允许自动下溯造型,除非明确指定一次这样的造型。

Class的newInstance()方法似乎是克隆(clone())一个对象的另一种手段。但两者是有区别的。利用newInstance(),我们可在没有现成对象供“克隆”的情况下新建一个对象。
利用newInstance()方法可以实现“虚拟构建器”。

用newInstance()创建的类必须有一个默认构建器。
没有办法用newInstance()创建拥有非默认构建器的对象,所以在Java 1.0中可能存在一些限制。
然而,Java 1.1的“反射”API(下一节讨论)却允许我们动态地使用类里的任何构建器。


编译器必须明确知道RTTI要处理的所有类。
(1) 基于组件的程序设计。“反射”提供了一种特殊的机制,可以侦测可用的方法,并产生方法名。通过Java Beans,可以为这种基于组件的程序设计提供了一个基础结构。
(2) 通过网络创建与执行位于远程系统上的对象。这就叫作“远程方法调用”(RMI),它允许Java程序使用由多台机器发布或分布的对象。

在Java 1.1中,Class类得到了扩展,可以支持“反射”的概念。
针对Field,Method以及Constructor类,它们都新增了一个库:java.lang.reflect。
这些类型的对象都是JVM在运行期创建的,用于代表未知类里对应的成员。
这样便可用构建器创建新对象,用get()和set()方法读取和修改与Field对象关联的字段,以及用invoke()方法调用与Method对象关联的方法。
此外,我们可调用方法getFields(),getMethods(),getConstructors(),分别返回用于表示字段、方法以及构建器的对象数组。
因此,匿名对象的类信息可在运行期被完整的揭露出来,而在编译期间不需要知道任何东西。

“反射”并没有什么神奇的地方。
通过“反射”同一个未知类型的对象打交道时,JVM只是简单地检查那个对象,并调查它从属于哪个特定的类(就象以前的RTTI那样)。
但在这之后,在我们做其他任何事情之前,Class对象必须载入。


RTTI和“反射”之间唯一的区别:
对RTTI来说:编译器会在编译期打开和检查.class文件。换句话说,我们可以用“普通”方式调用一个对象的所有方法;
对“反射”来说:.class文件在编译期间是不可使用的,而是由运行期环境打开和检查。

Class.forName()产生的结果不能在编译期间获知,所以所有方法签名信息都会在运行期间提取。可对一个编译期完全未知的对象进行实际的设置以及发出方法调用。

Java的要求是让我们尽可能地采用多形性,只有在极特别的情况下才使用RTTI。

RTTI的优点:
若基础类来自一个库,或者由别的什么东西控制着,RTTI便是一种很好的解决方案:可继承一个新类型,然后添加自己的额外方法。在代码的其他地方,可以侦测自己的特定类型,并调用那个特殊的方法。这样做不会破坏多形性以及程序的扩展能力,因为新类型的添加不要求查找程序中的switch语句。但在需要新特性的主体中添加新代码时,就必须用RTTI侦测自己特定的类型。
从某个特定类的利益的角度出发,在基础类里加入一个特性后,可能意味着从那个基础类衍生的其他所有类都必须获得一些无意义的“鸡肋”。这使得接口变得含义模糊。RTTI提供了一个更合理的解决方案,可将方法置入特定的类中——这样做是可行的。
RTTI有时能解决效率问题。

 

Class.forName用的是java的动态加载机制(RTTI),和反射我觉得是两码事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值