在运行期分析类和使用类的能力称之为反射。
Class类
要理解和使用反射,需要先认识一下 Class 类和 Class 对象。
这个 Class 类和 class 关键字可不同,Class 对象是与之相对应的对象在运行时的类型标识,是程序访问该对象的类型数据的外部接口。
简单来讲,就是:
在 Java 程序运行时,对于任意的一个类或者是对象,我们都能够通过 Class 对象来获取它的类名、属性和方法等信息。
在 API Doc 中,有如下描述:
Class类的实例表示正在运行的 Java 应用程序中的类和接口。
枚举是一种类,注释是一种接口。
数组也是一种类,所有具有相同元素类型和维数的数组都共享该Class对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示为Class对象。
Class 类没有公共构造方法,因此不能显式地构造一个 Class 对象。Class 对象是在类加载阶段由 JVM 调用类加载器(ClassLoader)中的 defineClass() 方法自动构造的。
另外,Class 对象还有点特殊:它是存放在方法区的,而不是存放在 Java 堆中。
Class 对象可以通过以下几种方法获取:
object.getClass()ClassName.classClass.forName(String className)
Class 类还是泛型的:Class<T> ,例如:String.class 实际上是 Class<String> 的一个对象。
反射
分析类
获取类名
使用 String getName() 方法可以获取此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void )名称。
数组的类名比较怪异,由表示该数组嵌套深度的 [ 字符和元素类型编码组成。
获取包
使用 Package getPackage() 可以获取类的包。
获取实现的接口
使用 Class<?>[] getInterfaces() 可以确定实现的接口。
获取类的修饰符
使用 int getModifiers() 可以获取以整数编码的 Java 语言修饰符,因此还需要使用 Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。
获取属性
使用 Field[] getFields() 可以获取所有 public 属性。
使用 Field[] getDeclaredFields() 可以获取所有方法,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。
还可以获取指定的属性:Field getDeclaredField(String name)Field getField(String name)
获取方法
使用 Method[] getMethods() 获取所有 public 成员方法。
使用 Method[] getDeclaredMethods() 可以获取所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
同样可以获取指定的方法:Method getMethod(String name, Class<?>... parameterTypes)Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取构造方法
使用 Constructor<?>[] getConstructors() 获取所有 public 构造方法。
使用 Constructor<?>[] getDeclaredConstructors() 获取所有构造方法,包括公共、保护、默认(包)访问和私有构造方法。
获取制定的构造方法:Constructor<T> getConstructor(Class<?>... parameterTypes)Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
使用类
构造一个实例
使用 Object 的 newInstance() 方法,它会自动去调用当前类的无参构造方法。如果不可访问,会拋异常的。
如果需要调用带参的构造方法,那么就要使用 Constructor 的 newInstance() 方法了。
如果不存在安全管理器,在获取了类的信息后,还可以通过 Field 、Method 、Constructor 的 setAccessible() 方法来突破访问权限的控制,来完成更多的操作。
修改属性的值
使用 Field 的 set(Object obj, Object value) 或者是 setXxx(Object obj, Xxx value) 方法。
调用任意方法
使用 Method 的 Object invoke(Object obj, Object... args) 方法。
这个方法的返回值是 Object 类型,因此,也就意味着往往需要我们自己进行类型转换,这是它的弱点。
更多内容
可以参考 java.lang.reflect 包,它提供了反射编程所需的类和接口。
实例
调用本地方法,使用
ClassLoader来加载创建出Class对象。1
Class action = Class.forName("ActionClass.class");
校验
ActionClass类是否为public类型,以确定类的执行权限。
如果不是public会拋SecurityException。
调用getDeclaredMethods()来获取Class中的所有方法(第一次调用时会缓存方法集合),找到签名一致的方法则复制返回一个Method对象。
否则,继续扫描父类、父接口中是否有该方法。
如果都没有,则抛出NoSuchMethodException。1
Method method = action.getMethod("execute", null); // 获取指定的方法
调用本地方法获取构造器,校验构造器对象的权限,并执行
newInstance()方法。1
Object object = action.newInstance();
调用方法。
1
method.invoke(object, null);