运行时类型识别,run-time type identification, RTTI:当只有一个指向对象基类的引用时, RTTI机制可以找出这个对象的确切类型。
多态机制
通常,也正是这样要求的,希望大部分代码尽可能少的了解对象的具体类型,而是只与对象家族中的一个通用表示打交道,这样的代码更容易写,更容易读,且更容易维护。设计也更容易实现,理解和改变。
但是,当遇到特殊的编程问题的时候,如果能够知道某个引用的确切类型就可以采用最简单的方式解决
传统的RTTI
假定我们在编译时和运行时已经知道了所有的类型
Class对象
类是程序的一部分,每个类都有一个Class对象。即每当编写并且编译一个新类的时候就会产生一个Class对 象 (更恰当的说,是被保存在一个同名的.class文件中)。运行时当我们需要这个类的对象的时候,运行这个程 序的 JVM首先会检查这个类的Class对象是否已经加载,如果尚未加载,JVM就会根据类名查找.class文件并将其 载入。
所以,Java程序并不是一开始就被完全加载的
程序中的static子句在类第一次被加载时执行,而Class对象仅在需要的时候才会被加载
Class.forName(str);
返回值是一个Class对象的引用,对象名为str
当找不到给定所要加载的类的时候,就会抛出异常
ClassNotFoundException
类字面常量:
Java提供的另一种来生成Class对象的引用,即使用“类字面常量”
如
ClassName.class;
这样做不仅简单,而且更加安全,因为在编译的时候就会收到检查,并且无需方法调用,所以也会更加高效
不仅适用于普通类,同样适用于接口,数组以及基本数据类型,另外,对于基本数据类型的包装类,还有一个 标准 字段TYPE。TYPE是一个引用,指向对应的基本数据类型的Class对象
类型转换前的检查
RTTI形式:
1):传统的类型转换。由RTTI确保类型转换的正确性,当执行了一个错误的类型转换的时候,会抛出异常 ClassCastException
2):代表对象类型的Class对象。通过查询Class对象可以获取运行时所需的信息
Java要执行类型检查。这通常被称为“类型安全的向下转型”。之所以是”向下转型“,是由于类层次结构图从来 都是这样排列的。编译器允许自由的做向上转型的赋值操作,然而如果不做显式的类型转换,编译器就不会 自 动做向下转型操作的。
3):关键字
instanceof,返回值为一个boolean值,告诉我们对象是不是特定的类型实例
如:
if(x instanceof Dog)
((Dog) x).speack();
进行向下转型前,如果没有其他信息可以告诉你这个对象时什么类型,那么使用instanceof是非常必要的,否则 会 得到ClassCastException异常。
拓展:
instanceof保持了类型的概念,他指的是”你是这个类吗?或者你是这个类的派生类吗?”,而“==”比较实际的Class对 象,就没有考虑到继承——它要么是这个确切的类型,要么不是。
RTTI语法
Java 是通过Class对象来实现RTTI机制的,即使我们只是做些诸如类型转换这类的事情。
Class类提供了很丰富的内容。
首先,需要获得指定适当的Class对象的引用。一种方法是用字符串和Class.forName()方法,这样做很方便,因为在 获取Class引用时就不需要该类型的对象。然而如果已经有了一个你感兴趣的类型的对象,那么就可以通过调用 getClass()来获取Class引用,这是Object根类提供的方法,他返回Class引用,用来表示对象的实际类型。
Class对象部分方法:
getSuperclass()获取基类信息
乍看起来,Class的
newInstance()方法似乎只是另一个克隆对象的方法(clone()),然而,即使没有原先任何对象 存在,也可以使用
newInstance()创建一个新的对象。这是一种“虚拟构造器”的途径,这使得你可以说“尽管我不知道 你是的准确类型是什么,但无论如何,但还是请正确的创建自己”。此外,使用
newInstance()而创建的类必须有一个 缺省的构造器。
反射:运行时的类信息
对于RTTI,可以告知我们某个对象的确切类型,但是这个类型在编译时必须是已知的,这样才能使用RTTI识别,并利 用这些信息处理一些事情,换句话说,在编译时,编译器必须知道所有要通过RTTI来处理的类
很少直接使用反射工具,他们在Java中是用来支持其他特性的。但是类方法提取器是一个很重要的作用