java的反射机制
什么是java.lang.Class类?
众所周知Java有个Object类,是所有Java类的继承根源,其内声明了数个应该在所有Java类中被改写的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class类的对象。
Class类十分特殊。它和一般class一样继承自Object,其实体用以表达Java程序运行时的class和interface,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void,也就是说运行时的class和interface,基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。 Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的一个Class object(对象),因此不能显式地声明一个Class对象。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
什么是反射机制?
Class类是Reflection起源。针对任何想探勘的class,唯有先为它产生一个Class object,接下来才能经由它唤起为数十多个的Reflection APIs。Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。
Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
Class类:代表一个类。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor 类:代表类的构造方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
反射机制有什么优势?
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法。
生成动态代理。
如何使用反射机制?
通过Class类获取成员变量,成员方法和构造器
首先是获取类的Class对象,因为它是Reflection API 中的核心类,有4中方式:
方法 | 举例 |
---|---|
运用.class 语法 | Class<?> classType= Boolean.class; System.out.println(classType); 输出:class java.lang.Boolean |
运用static method Class.forName() | Class<?> classType = Class.forName(“java.lang.Boolean”); System.out.println(classType5); 输出:class java.lang.Boolean |
运用primitive wrapper classes的TYPE 语法(这里返回的是原生类型,和Boolean.class返回的不同) | Class<?> classType= Boolean.TYPE; System.out.println(classType3); 输出:boolean |
调用getClass | Boolean var= true; Class<?> classType = var.getClass(); System.out.println(classType); 输出:class java.lang.Boolean |
然后获取类的属性和方法以及构造器:
public String getName():获得类的完整名字。
public Field[] getFields():返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
public Field getField(String name) :返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
public Field[] getDeclaredFields():返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
public Field getDeclaredField(String name): 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
|
|
public Method getMethod(String name,Class<?>… parameterTypes):返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
public Method[] getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
public Method getDeclaredMethod(Stringname,Class<?>… parameterTypes):返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
public Method[] getDeclaredMethods():返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
|
|
public Constructor
public Constructor<?>[] getConstructors():返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
public Constructor
public Constructor<?>[] getDeclaredConstructors():返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法。
示例代码:
|
|
获取类、属性、方法的修饰域
类Class、Method、Constructor、Field都有一个public方法int getModifiers()。该方法返回一个int类型的数,表示被修饰对象( Class、 Method、 Constructor、 Field )的修饰类型的组合值。
|
|
新建类的实例
|
|
动态创建和访问数组元素
|
|
示例代码使用:
|
|
动态创建代理类
代理模式:代理模式的作用=为其他对象提供一种代理以控制对这个对象的访问。
代理模式的角色:
抽象角色:声明真实对象和代理对象的共同接口。
代理角色:代理角色内部包含有真实对象的引用,从而可以操作真实对象。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
java.lang.reflect.Proxy | Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类 |
InvocationHandler | 是代理实例的调用处理程序 实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 |
动态Proxy是这样的一种类:
它是在运行生成的类,在生成时你必须提供一组Interface给它,然后该class就宣称它实现了这些interface。你可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
|
|
|
|
|
|
另外附录一个例子说明动态代理的用处
我们有一个字体提供类,有多个方法
|
|
|
|
|
|
|
|
现在我们希望给他加上一个日志系统,最直接的对FontProviderFromDisk中的所有方法修改,加入日志,显然这是很繁琐的。另外有两种方式
方法一:静态代理
首先定义LogProvider类,实现FontProvider接口,充当代理,当然也要修改ProviderFactory工厂。
|
|
|
|
问题来了,假如又有一个ImgeProvider接口以及实现类ImgeProviderFromDisk也需要加入日志系统,我们这时要扩展LogProvider,实现ImgeProvider接口。随着越来越多的类需要加入日志系统,代理类LogProvider的实现会越来越繁多。那么动态代理就体现其优越了。
方法二:动态代理
原先接口实现类如ImgeProviderFromDisk,FontProviderFromDisk的代码不需要改动,同时代理类的代码实现后也不需要改动了。相比较静态代理来说,代理类轻松很多了。
|
|
|
|
参考:
http://lavasoft.blog.51cto.com/62575/43218/
http://blog.csdn.net/yongjian1092/article/details/7364451
http://azrael6619.iteye.com/blog/429797
http://www.cnblogs.com/yaozhongxiao/archive/2013/05/21/3091353.html