运行时类型信息可以使得在程序运行时发现和使用类型信息。Java在运行时识别对象和类信息的两种方式:一是RTTI,假定我们在编译时就知道了所有类型;二是“反射”机制,允许在运行时发现和使用类信息。
1、为什么需要RTTI
RTTI:Run-Time Type Identification,运行时对象类型识别。
在Java中,所有的类型转换都是在运行时进行正确性检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。使用RTTI,你可以查下某个对象的引用所指向的对象的确切类型,然后选择或者剔除特例。
2、Class对象
Class包含了与类有关的信息,事实上Class对象就是用来创建类的所有的“常规”对象的。Java使用Class对象来执行其RTTI,即使你正在执行的是类似转型这样的操作。Class类还拥有大量的使用RTTI的其他方式。
每当编写并且编译了一个新类,就会产生一个Class对象(是被保存在.class文件中)。为了生成这个对象,运行这个程序的Java虚拟机将使用被称为“类加载器”的子系统。类加载器子系统可以包含一条类加载器链,但是只有一个原生类加载器。原生加载器通常从本地盘加载,加载的都是可信类,包括Java API类。
所有的类都是对其第一次调用时,动态加载到JVM,当程序创建第一个对类的静态成员的引用时,就会加载整个类。因此证明类的构造器也是类的静态方法。因此,使用new操作符创建类的新对象也会被当作对类的静态成员的引用。类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认类加载器就会根据类名查找.class文件。在这个类的字节码(.class文件)被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良代码。
Class类的一些方法:
forName(“类名”)返回一个Class对象的引用,主要被用作如果类没有被加载就加载它
getName()返回全限定的类名
getSimpleName()返回不含包名的类名
getCanonicalName() 返回全限定类名
getInterfaces()返回Class对象的接口们
getSuperclass()返回直接基类
newInstance()得到Object引用(实现“虚拟构造器”的一种途径,它允许你声明:“我不知道你的确切类型,但是无论如何都要争取”,newInstance()创建的类必须有默认构造器)
2.1 类字面常量
使用类字面常量可以生成对Class对象的引用。比如:Shapes.class
对于类字面常量,在编译时就会检查,更高效安全。类字面常量不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。
当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。为使用类而做准备工作包含的三个步骤:
1)加载 查找字节码,并创建class对象
2)链接 验证字节码,为静态域分配空间
3)初始化 如果该类有超类,对其初始化,执行静态初始化器和静态初始化块
初始化有效的实现了尽可能的“惰性”,使用.class语法来获得对类的引用并不会引发初始化。
如果一个static final是“编译期常量”,那么这个值不需要对整个类进行初始化就可以被读取。但是,如果只是将一个域设置为static和final的,还不足以确保这种行为,因为它不是一个编译期常量,这样还是会对类进行加载。如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化存储空间)。
2.2 泛化的Class引用
Class引用总是指向某个Class对象,表示的是它所指对象的确切类型。使用泛型引用,可以限定class引用的类型,使其不能再指向其他类型,是的编译器强制执行额外的类型检查。普通的class引用没有这个限制。
Class c = Cat.class;
为了放松限制,可以使用通配符,符号为“?”,表示任何类型。使用通配符产生的class引用,可以指向其它类型。例如:
Class<?> intClass = int.class;
使用“?extends BaseType”,可以创建一个范围,表示该类或该类的子类。向Class添加泛型语法的原因是为了提供编译期类型检查。例如:
Class<? extends Number> bounded = int.class;
将泛型语法用于class对象,newInstance()将返回该对象的确切类型。
Class c = Cat.class;
Cat mimi = c.newInstance();
2.3 新的转型语法
cast()方法接受参数对象,并将其转型为Class引用的类型。要转成类型的对象 = cast(被转对象);
Cat c= new cat();
Class p = Pet.class;
Pet pet = p.cast(c);
这个方法很繁琐,除了极端罕见情况外几乎用不到,毕竟平时使用 Pet pet = (Pet)c; 简单一句就可以了