Java的反射机制

发布时间 2023-10-08 15:11:33作者: Ohana+

Java的反射机制

前言

能够分析类能力的程序称为反射,反射机制可以用来:

  • 在运行程序时分析类的能力;
  • 在运行时检查对象;
  • 实现泛型数组操作代码;
  • 利用Method对象;

Class类

在程序运行期间,Java运行时系统始终为所有对象维护一个「运行时类型信息标识」。

这个信息会跟踪每个对象所属的类,Java虚拟机利用运行时类型信息标识选择要执行的正确的方法。

保存这些信息的类名为Class,可以使用这个特殊的Java类访问这些信息。

虚拟机为每个类型管理一个唯一的Class对象,可以利用「==」运算符比较两个Class对象的类型。

可以通过Object类中的getClass()方法返回一个Class类的实例。

String str = new String();
Class strClass = str.getClass();

可以使用Class类型中的forName()静态方法,通过指定类的全限定类名获取该指定类的Class实例。

String className = "java.util.Random";
Class randomClass = Class.forName(className);

可以使用「类型.class」的方式获取类型的Class实例。

Class randomClass = Random.class;
Class intClass = int.class;
Class doubleArrClass = Doublue[].class;

Class类实际上是一个泛类型。(例如:String.class的类型是Class

可以通过Class对象的getConstructor方法得到一个Constructor类型的对象,然后使用该对象的newInstance方法来构造一个实例。(要求必须存在一个无参构造方法)

Class strClass = String.class;
Constructor strConstructor = strClass.getConstructor();
String str = strConstructor.newInstance();

资源

类通常有一些关联的数据文件,在Java中,这些文件被称为「资源」。

Class类提供了一个有用的服务,可以用来查找资源文件:

  1. 获得拥有资源的类的Class实例;
  2. Class实例的getResource("文件名"),可以获取资源的URL位置;
  3. Class实例的getResourceAsStream方法得到一个输入流来读取文件中的数据;

文件的自动装载是利用资源加载特性完成的。

利用反射分析类的能力

在java.lang.reflect包中,有三个类Filed、Method和Constructor分别描述类的字段、方法和构造器:

  • 这三个类都有一个叫作getName的方法,用来放回字段、方法和构造器的名称;

  • 这三个类都有一个叫作getModifiers的方法,返回一个整数,描述使用的访问权限修饰符;

  • java.lang.reflect包中的Modifier类,该类中的静态方法可以用来分析getModifiers方法的返回值;

  • Filed类有一个getType方法,用来返回描述字段类型的Class对象;

  • Class类中的getFields、getMethods和getConstructors方法将分别返回这个类支持的「公共字段」、「公共方法」和「公共构造器」的数组,包括其超类的公共成员;

  • Class类的getDeclareFields、getDeclareMethods和getDeclareConstructors方法将分别返回类中声明的全部字段、方法和构造器的数组,但是不包括超类的成员;

  • Class类的getPackageName方法可以获取这个类所在的包名;

使用反射在运行时分析对象

利用反射机制可以查看在编译时还不知道的对象字段。

Field类中的get(obj)方法可以获得obj对象当前字段的值。

// Employee(name,age)
Employee employee = new Employee("Jack",25);
Class emplClass = employee.getClass();
Field nameFiled = emplClass.getField("name");
Object nameObject = nameFiled.get(employee);

Filed类中的set(obj,value)可以给obj对象当前字段的值设置为value。

// Employee(name,age)
Employee employee = new Employee("Jack",25);
Class emplClass = employee.getClass();
Field nameFiled = emplClass.getField("name");
nameFiled.set(employee,"ZhangSan");

只能对公共成员字段使用get和set方法,否则会跑出IllegalAccessException异常。

Java的安全机制允许查看一个对象有哪些字段,但是除非拥有访问权限,否则将不允许读写那些字段的值。

反射机制的默认行为受限于Java的访问控制,可以调用Filed、Method和Constructor对象的setAccessible方法覆盖Java的访问控制。

// Employee(name,age)
Employee employee = new Employee("Jack",25);
Class emplClass = employee.getClass();
Field nameFiled = emplClass.getField("name");
// 覆盖这个字段的访问权限
nameField.setAccessible(Boolean.TRUE);
// 可以正常访问name这个私有成员字段
Object nameObject = nameFiled.get(employee);

调用任意方法和构造器

利用Method类中的invoke方法,可以调用包装在当前Method对象中的方法:

  • invoke方法的签名是:Object invoke(Object, Object... args)
  • 第一个参数是隐式参数,其余的对象提供了显式参数
  • 对于静态方法,第一个参数可以忽略,即可以设置为null
// void doSomething(String,Integer)
Method doSomeThingMethod = classObject.getMethod("doSomething");
doSomeThingMethod.invoke("some thing",12);

可以通过调用Class对象的getDeclareMethods方法获得Method对象组。

Method[] methodArray = classObject.getDeclareMethods();

可以通过调用Class对象的getMethod方法获得指定的Method对象。

getMethod方法的签名是:Method getMethod(String name, Class... parameterTypes)

Method method = classObject.getMethod("getName");

可以使用类似的方法调用任意的构造器,需要将构造器的参数类型提供给Class.getConstructor方法,并把参数值提供给Constructor.newInstance方法。

Constructor constructor = classObject.getConstructor(Long.class);
Object obj = constructor.newInstance(20L);