Java漫游笔记-03-04-反射和Class类

在运行期分析类和使用类的能力称之为反射。

Class类

要理解和使用反射,需要先认识一下 Class 类和 Class 对象。
这个 Class 类和 class 关键字可不同,Class 对象是与之相对应的对象在运行时的类型标识,是程序访问该对象的类型数据的外部接口。
简单来讲,就是:
在 Java 程序运行时,对于任意的一个类或者是对象,我们都能够通过 Class 对象来获取它的类名、属性和方法等信息。
在 API Doc 中,有如下描述:

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
枚举是一种类,注释是一种接口。
数组也是一种类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
基本的 Java 类型( booleanbytecharshortintlongfloatdouble )和关键字 void 也表示为 Class 对象。

Class 类没有公共构造方法,因此不能显式地构造一个 Class 对象。
Class 对象是在类加载阶段由 JVM 调用类加载器(ClassLoader)中的 defineClass() 方法自动构造的。
另外,Class 对象还有点特殊:它是存放在方法区的,而不是存放在 Java 堆中。

Class 对象可以通过以下几种方法获取:

  • object.getClass()
  • ClassName.class
  • Class.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)

使用类

构造一个实例

使用 ObjectnewInstance() 方法,它会自动去调用当前类的无参构造方法。如果不可访问,会拋异常的。
如果需要调用带参的构造方法,那么就要使用 ConstructornewInstance() 方法了

如果不存在安全管理器,在获取了类的信息后,还可以通过 FieldMethodConstructorsetAccessible() 方法来突破访问权限的控制,来完成更多的操作。

修改属性的值

使用 Fieldset(Object obj, Object value) 或者是 setXxx(Object obj, Xxx value) 方法。

调用任意方法

使用 MethodObject invoke(Object obj, Object... args) 方法。
这个方法的返回值是 Object 类型,因此,也就意味着往往需要我们自己进行类型转换,这是它的弱点。

更多内容

可以参考 java.lang.reflect 包,它提供了反射编程所需的类和接口。

实例

  1. 调用本地方法,使用 ClassLoader 来加载创建出 Class 对象。

    1
    Class action = Class.forName("ActionClass.class");
  2. 校验 ActionClass 类是否为 public 类型,以确定类的执行权限。
    如果不是 public 会拋 SecurityException
    调用 getDeclaredMethods() 来获取 Class 中的所有方法(第一次调用时会缓存方法集合),找到签名一致的方法则复制返回一个 Method 对象。
    否则,继续扫描父类、父接口中是否有该方法。
    如果都没有,则抛出 NoSuchMethodException

    1
    Method method = action.getMethod("execute", null);  // 获取指定的方法
  3. 调用本地方法获取构造器,校验构造器对象的权限,并执行 newInstance() 方法。

    1
    Object object = action.newInstance();
  4. 调用方法。

    1
    method.invoke(object, null);