本章讨论Java如何让我们在运行时识别对象和类的信息,一种是传统的RTTI(Run-Time Type Information),它假定我们在编译时已经知道了所有的类型;另一种是反射机制,它允许我们在运行时发现和使用类的信息
面对对象编程中基本的目的就是:让代码只操纵对基类的引用,减少了解对象的具体类型,只和通用表示(基类)打交道,这样代码更容易维护、改变,实现上述功能的机制就是多态,他能帮助找到具体类型和具体方法,实现动态绑定
为了理解RTTI的工作原理,就要知道类型信息在运行时是通过称为Class对象的特殊对象完成的,它被用来创建类的所有常规对象,记录了类有关信息,也就是说每创建一个类,就会产生一个Class对象(也就是同名的.class文件,关于JVM中加载该文件的机制可参见《深入理解Java虚拟机》,最简单的机制就是程序对于类的静态成员应用时,类加载器就会加载该类对应的.class文件,由此可以看出构造器默认就是静态成员方法)
Class对象和其他对象一样,可以获取并操作它的引用,也可以使用它的很多方法
forName(String)
:返回一个Class对象的引用,如果在运行时使用类型信息,就必须获得恰当的Class对象的引用,这个方法就是实现上述功能的简便途径getInterfaces()
:返回的是Class对象,表示对象包含的接口getSuperclass()
:返回直接基类,进而可以查询发现一个对象的完整类继承结构newInstance()
:实现“虚拟构造器”,其允许你申明不知道确切类型,但是无论如何都可以创建自己,这个类必须带有默认的构造器
除了forName()
之外,还可以使用类字面常量来生成对Class对象的引用,即demo.class
,类字面还可以应用于接口、数组以及基本数据类型
Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象,通过添加泛型语法可以对Class引用所指向的Class对象的类型进行限定
RTTI形式
- 传统的类型转换,由RTTI确保类型转换的正确性
- 代表对象的类型的Class对象,通过查询Class对象可以获取运行时信息
- instanceof
注册工厂参见
上述的方法中RTTI能告诉对象的确切类型的前提是这个类型在编译时必须已知,但有时候在编译时程序无法获知对象的类信息(如RMI),这时候就需要使用反射概念,由Class类和java.lang.reflect类库一起实现反射概念,类库中包含Field、Method、Constructor类,这些类型的对象由JVM在运行时创建,用于表示未知类的成员,并通过相应方法就能在运行时确定匿名对象的类信息
反射没有什么神秘之处,和RTTI真正的区别就在于.class文件在编译期是不可以获取的,要由JVM在运行时打开和检查.class文件。反射是为了支持Java其他特性的,如对象序列化和JavaBean
动态代理:可以动态创建代理并且处理对所代理方法的调用,在动态代理上所做的调用都会被重定向到单一的调用处理器上,它的工作是揭示调用类型并确定相应的对策,Spring AOP默认使用动态代理
通过调用静态方法Proxy.newProxyInstance()
创建动态代理,这个方法需要一个类加载器,一个需要该代理实现的接口列表以及InvocationHandle接口的一个实现,这样这个动态代理就可以把所有调用重定向到调用处理器上,因此在调用处理器的构造器传递一个实际对象,由它来实际执行代理,即将请求转发。在调用处理器只需要实现一个invoke()
方法,在内部,接口的调用将被重定向对代理的调用
代理模式参见
引入反射也会带来坏处,这样不管是private、protected方法都可以通过反射机制访问到,但反射带来的好处是不言而喻的