以下内容大部分取于《Java编程思想第四版》
RTTI简介
RTTI(Run-Time Type Information),运行时类型信息使得你可以在程序运行时发现和使用类型信息。主要有两种方式:
- 传统的RTTI:假定我们在编译时已经知道了所有的类型;
- 反射机制:允许我们在运行时发现和使用类的信息。
RTTI的最基本使用形式,所有的类型转换都是在运行时能正确性检查的。
Class对象
Class对象就是用来创建类的所有的“常规”对象的,Java使用Class对象来执行其RTTI,即使你正在执行的是类似转型这样的操作。
每个类都有一个Class对象,.java文件编译生成一个.class文件,则该类的Class对象保存在.class文件中。为了生成这个类的“常规”对象,运行则个程序的JVM将通过加载器加载这个类。所有的类都是在其第一次使用时,动态加载到JVM中的,当程序第一次使用类的静态成员的时,就会加载这个类,这个也证明构造器也是类的静态方法。因此,Java程序在它开始之前并非完全被加载,各个部分在必须时才加载的。
类加载器首先检查这个类的Class对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名来查找.class文件,后续有验证等操作。一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用,Class类的forName()静态方法就是实现此功能的便捷途径。当然当你持有某个对象时,也可以调用getClass()方法来获得Class对象的引用。另外还有一种方式可以获得Class对象的引用,即一个类的字面常量:MyClass.class,不仅更简单也更安全。
通过Class对象,你可以发现你想要了解的类型的所有信息
RTTI形式
- 传统的类型转换,(MyClass)由RTTI确保类型转换的正确性;
- 代表对象类型的Class对象,通过它可以获取运行时所需的信息;
- 关键字instanceof,if(x instanceof Y),检查对象x是否从属于Y类。
反射
- 如果不知道某个对象的确切类型,RTTI可以告诉你。但是有一个限制,这个类型在编译时必须已知。即在编译时,编译器必须知道所有要通过RTTI来处理的类。
- 人们想要在运行时获取类的信息的动机有:假设你获取了一个指向某个并不在你的程序空间中的对象的引用,或者希望提供在跨网络的远程平台创建和运行对象的能力(RMI)。Class类和java.lang.reflect类库一起对反射的概念进行了支持。通过上诉类与类库,匿名对象的类的信息就能在运行时被完全确定了下来,而在编译时不需要知道任何事情。
- 当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。在用它做其他事情之前必须先加载那个类的Class对象。因此,那个类的.class文件对于JVM来说必须是可获取的:要么在本地,要么通过网络获得。
- 所以RTTI和反射之间的真正区别只在于,对于RTTI来说,编译器是在编译时打开和检查.class文件,即我们可以用普通方式调用对象的所有方法;而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。通常你不需要直接使用反射工具,但是它们在你需要创建更多动态的代码时会很有用。
小结
也许你的程序漂亮地运行了多态,但其中某个对象以极端缺乏效率的方式达到这个目的,你可以挑出这个类,使用RTTI,而且为其编写一段特别的代码提高效率。然而需要注意,不要太早地关注程序的效率问题,这是个有人的陷阱,最好先让程序运行起来,然后再考虑它的速度,如果要解决效率问题可以使用profiler。