Java反射

Java 反射是一种强大的机制,允许在运行时检查、访问和操作类、对象、方法、字段等。它使得程序可以在不事先知道类的具体信息的情况下,动态地操作类和对象。Java 反射的主要功能包括:

  1. 获取类的信息:通过反射可以获取类的名称、包名、父类、接口、字段、方法等的信息。
  2. 创建对象:使用反射可以在运行时动态地创建类的实例,即使类名在编译时不是固定的。
  3. 访问字段和方法:反射允许访问对象的字段值和调用对象的方法,包括私有字段和方法。
  4. 调用构造函数:通过反射可以实例化类的对象并调用其构造函数,可以是无参构造函数或有参构造函数。
  5. 动态代理:反射可以用于创建动态代理对象,用于代理和拦截方法调用,实现 AOP(面向切面编程)等功能。
  6. 获取注解信息:反射可以获取类、方法、字段等上的注解信息,从而实现更灵活的配置和编程。
  7. 动态加载类:反射可以通过类加载器在运行时动态加载类,这对于插件化、模块化等场景非常有用。
  8. 修改字段值:反射可以修改类的字段值,包括私有字段。
  9. 操作数组:通过反射可以操作数组,包括创建数组对象、获取元素值、修改元素值等。
  10. 动态修改类的结构:反射允许在运行时动态地修改类的结构,例如增加新的方法、字段等。

需要注意的是,虽然 Java 反射功能非常强大,但由于它在运行时动态地进行操作,可能会导致性能上的一些开销,同时也会破坏一部分封装性。因此,在使用反射时需要权衡其优势和劣势,并选择适当的情况下使用。反射是一种功能强大且复杂的机制。 使用它的主要人员是工具构造者,而不是应用程序员。

Java 反射类具有一些优点和缺点,下面分别列举和解释:

优点:

  1. 动态性和灵活性: 反射允许在运行时动态地获取和操作类的信息,这使得程序可以在不事先知道类的具体信息的情况下进行操作,从而实现更加灵活的编程。
  2. 适用于框架和库: 反射在很多框架和库中被广泛使用,例如依赖注入、ORM(对象关系映射)、AOP(面向切面编程)等,这些场景需要在运行时对类和对象进行操作,反射提供了便捷的实现方式。
  3. 扩展性: 反射使得程序可以在运行时动态加载和使用类,这对于插件化、模块化等需求非常有用,可以实现动态加载功能模块。
  4. 工具开发: 反射可以用于开发一些工具,如 Java 编译器、分析工具、自动生成代码等,这些工具需要在运行时分析和操作类的结构和信息。

缺点:

  1. 性能开销: 反射操作通常比直接调用代码要慢,因为它需要在运行时进行类型检查和动态分派。这可能在某些性能敏感的场景中引入额外的开销。
  2. 编译时检查失效: 使用反射可以绕过编译时的类型检查,可能会在运行时出现类型不匹配或错误,这可能导致难以发现和调试的问题。
  3. 安全问题: 反射可以访问私有字段和方法,可能破坏类的封装性。此外,不当的反射使用可能会引起潜在的安全风险。
  4. 代码可读性下降: 过度使用反射可能导致代码的可读性下降,因为不再能在代码中直接看到类和方法的使用。
  5. 维护困难: 反射操作可能使代码变得难以维护,因为类的结构和行为在运行时可以改变,而不是在编译时固定。

Class类

在程序运行期间,Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标识。可以通过专门的 Class,类访问这些信息,Object 类中的 getClass( ) 方法将会返回一个 Class 类型的实例。

1
2
Employee e;
Class cl = e.getClass();

虚拟机为每个类型管理一个 Class 对象。 因此,可以利用 =运算符实现两个类对象比较的操作。 例如,

if (e.getClass() == Employee.class) . . .

还有一个很有用的方法 newlnstance( ), 可以用来动态地创建一个类的实例,例如,

e.getClass().newlnstance();

反射语法

获取Class对象

Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.知道具体类的情况下可以使用
Class alunbarClass = TargetObject.class;

//2.通过 Class.forName()传入类的全路径获取:
String className = "com.example.MyClass";
Class<?> classObj = Class.forName(className);

//3.通过对象实例的instance().getClass获取
MyClass obj = new MyClass();
Class<?> classObj = obj.getClass();

//4.通过类加载器xxxClassLoader.loadClass()传入类路径获取:
ClassLoader classLoader = getClass().getClassLoader();
Class<?> classObj = classLoader.loadClass("com.example.MyClass");

反射基本操作

假设有一个这样的类,需要被反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.javaguide;

public class TargetObject {
private String value;

public TargetObject() {
value = "JavaGuide";
}

public void publicMethod(String s) {
System.out.println("I love " + s);
}

private void privateMethod() {
System.out.println("value is " + value);
}
}

下面是反射操作这个类的方法以及参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package cn.javaguide;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
Class<?> targetClass = Class.forName("cn.javaguide.TargetObject");
TargetObject targetObject = (TargetObject) targetClass.newInstance();
/**
* 获取 TargetObject 类中定义的所有方法
* 使用 getDeclaredMethods() 方法可以获取类中声明的所有方法,包括公共方法、私有方法等。
* 这里如果使用 getMethods() 方法可以获取类中声明的所有公共方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}

/**
* 获取指定方法并调用
* 使用 getDeclaredMethod("methodName", parameterTypes) 方法也可以获取指定名称和参数列表的方法。
* 使用 Method 对象的 invoke() 方法可以调用方法。
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",String.class);
publicMethod.invoke(targetObject, "JavaGuide");

/**
* 获取指定参数并对参数进行修改。
* 使用 getDeclaredField() 方法获取字段信息
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改我们取消安全检查
field.setAccessible(true);
field.set(targetObject, "JavaGuide");

/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}