Life has its own fate, and meeting may not be accidental.

0%

Java安全-反射篇

什么是java反射?

Java反射(Reflection)是Java编程语言的一个强大特性,它允许程序在运行时访问和操作类、接口、字段、方法等的元数据。反射API提供了一组类和方法,使得程序能够查询和使用类的信息,以及在运行时动态地创建对象、调用方法、访问字段等。
使用Java反射,你可以实现以下功能:

  1. 在运行时获取类的信息:可以通过Class类或Class.forName()方法获取类的Class对象,然后查询类的定义信息,如类名、父类、实现的接口、构造函数、字段和方法等。
  2. 动态创建对象实例:使用Class对象的newInstance()方法可以创建类的实例,即使在编译时不知道具体的类名。
  3. 动态调用方法:可以通过Method对象的invoke()方法动态地调用对象的方法,无论这些方法是公共的、受保护的、默认的还是私有的。
  4. 动态访问和修改字段:使用Field对象可以动态地读取或修改对象的字段值,无论字段的访问权限如何。
  5. 创建代理对象:利用InvocationHandler和Proxy类,可以在运行时创建接口的代理实例,而无需在编译时定义具体的实现类。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) {
try {
// 获取Class对象
Class<?> clazz = Class.forName("java.lang.Runtime");

// 使用getMethod获取Runtime类的getRuntime().exec方法
Method getRuntimeMethod = clazz.getMethod("getRuntime");

// 使用invoke方法动态调用getRuntime方法
Object result = getRuntimeMethod.invoke(clazz);
Method execMethod = clazz.getMethod("exec", String.class);
execMethod.invoke(result,"calc.exe");

} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

简化写法

1
2
Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec",String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz),"calc.exe");

这里用到了 getMethod 和 invoke 方法。

getMethod

在Java中,getMethod是java.lang.Class类的一个方法,它用于在运行时获取类中特定名称和参数类型的Method对象。这个Method对象代表了类中的一个方法。
getMethod 的作用是通过反射获取一个类的某个特定的公有方法。使用getMethod方法,你可以在运行时动态地调用一个对象的方法,即使在编译时你不知道具体要调用哪个方法。getMethod方法有几种重载形式,最常见的两种是:

  1. getMethod(String name):通过方法名获取一个无参方法。
  2. getMethod(String name, Class<?>… parameterTypes):通过方法名和参数类型数组获取一个方法。

我们使用最简单的,也就是第一个,它只有一个参数,类型是String,所以我们使用 getMethod(“exec”, String.class) 来获取 Runtime.exec 方法。


这个例子中我们首先获取了String类的Class对象,然后使用getMethod方法获取了getRuntime方法的Method对象。最后,我们通过invoke方法动态地调用了exec()方法,并打印了结果。

invoke

在Java中,invoke方法是java.lang.reflect.Method类的一个方法,它用于动态调用一个对象的方法。这个方法是Java反射机制的一部分,允许程序在运行时确定和调用方法,而不需要在编译时硬编码方法的调用。
invoke方法的基本语法如下:

1
public Object invoke(Object obj, Object... args)
  • obj 参数是要在其上调用方法的对象。如果方法是一个静态方法,那么这里传入的是类本身(Class 对象)。
  • args 参数是一个可变参数,包含了要传递给方法的参数。如果调用的方法不需要参数,这里可以传入一个空数组。

invoke 方法可能会抛出几个异常:

  • IllegalAccessException:如果方法不可访问,比如它是私有的。
  • InvocationTargetException:如果方法本身抛出了异常,这个异常会被封装在InvocationTargetException中。可以通过调用getCause方法来获取原始的异常。
  • IllegalArgumentException:如果传递给invoke的参数不正确,比如参数数量不匹配或者类型不兼容。

使用例子如下:

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
import java.lang.reflect.Method;

public class InvokeExample {
public static void main(String[] args) {
try {
MyClass math = new MyClass(); // 创建一个Math类的对象
Method method = MyClass.class.getMethod("add", int.class, int.class); // 获取Math类的add方法
Object result = method.invoke(math, 5, 3); // 动态调用add方法
System.out.println("Result: " + result); // 输出结果

Method staticMethod = MyClass.class.getMethod("staticMethod"); // 另一个例子:调用静态方法
Object staticResult = staticMethod.invoke(null);
System.out.println("Static Result: " + staticResult);
} catch (Exception e) {
e.printStackTrace();
}
}
}

class MyClass {
public int add(int a, int b) {
return a + b;
}

public static String staticMethod() {
return "Hello from static method!";
}
}

我们首先通过getMethod获取了MyClass的add方法和staticMethod方法的Method对象。然后,我们使用invoke方法分别调用了这两个方法,并打印了结果。对于staticMethod,我们传入null作为第一个参数,因为静态方法不需要对象实例就可以调用。