@Author: Patrilic
@Time: 2020-3-18 17:43:11

0x00 前言

反射是Java的一个高级特性,指在运行状态中对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,创建实例,修改类成员变量等等。 这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

0x01 相关类

类名 用途
Class类 类的实体,在运行的Java应用程序中表示类和接口
Method类 类的方法
Construct类 类的构造方法
Field类 类的成员变量

Get Class

获取类的方式通常有三种

1
2
3
Class  runtimeClass1 = Class.forName(className);
Class runtimeClass2 = java.lang.Runtime.class;
Class runtimeClass3 = ClassLoader.getSystemClassLoader().loadClass(className);

需要注意的是,在获取数组类型的Class对象时,需要使用特殊的Java描述符

1
2
Class<?> doubleArray = Class.forName("[D");
Class<?> cStringArray = Class.forName("[[Ljava.lang.String;");

Get Construct

首先跳到java.lang.Runtime的源码处,可以观察到Runtime类的构造方法是private属性的,也就是说不允许其他人创建实例。
e488ae699f0ee7f64c5842bfbd585adb

也就是说,在没有import java.lang.Runtime的时候,我们是不能够去new一个新的Runtime对象的

反射机制提供了两种方法可以获取类的构造方法:

  • runtimeClass1.getDeclaredConstructor
  • runtimeClass1.getConstructor // 不能获取private方法

获取Constuct后,使用constructor.newInstance()实例化

当我们没有访问构造方法权限时我们应该调用constructor.setAccessible(true)修改访问权限就可以成功的创建出类实例了

Get Method

获取当前类所有的成员方法:

1
Method[] methods = runtimeClass1.getDeclaredMethods();

获取当前类指定的成员方法:

1
Method method = clazz.getDeclaredMethod("方法名");

通过method.invoke可以调用方法:

1
method.invoke(方法实例对象, 方法参数值,多个参数值用","隔开);

method.invoke的第一个参数必须是类实例对象,如果调用的是static方法那么第一个参数值可以传null,因为在java中调用静态方法是不需要有类实例的,因为可以直接类名.方法名(参数)的方式调用

Get Field

获取当前类的所有成员变量:

1
Field fields = clazz.getDeclaredFields();

获取当前类指定的成员变量:

1
Field field  = clazz.getDeclaredField("变量名");

获取当前类的所有成员变量:

1
Field fields = clazz.getDeclaredFields();

获取当前类指定的成员变量:

1
Field field  = clazz.getDeclaredField("变量名");

当我们没有修改的成员变量权限时可以使用: field.setAccessible(true)的方式修改为访问成员变量访问权限

修改final关键字修饰的变量时:

1
2
3
4
5
6
7
8
9
10
11
// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");

// 设置modifiers修改权限
modifiers.setAccessible(true);

// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

// 修改成员变量值
field.set(类实例对象, 修改后的值);

0x02 反射java.lang.Runtime

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
package com.patrilic.reflect;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class RuntimeClass {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String className = "java.lang.Runtime";
Class runtimeClass1 = Class.forName(className);
Class runtimeClass2 = java.lang.Runtime.class;
Class runtimeClass3 = ClassLoader.getSystemClassLoader().loadClass(className);

System.out.println(runtimeClass1);
System.out.println(runtimeClass2);
System.out.println(runtimeClass3);

Constructor constructor = runtimeClass1.getDeclaredConstructor();
constructor.setAccessible(true);

// 创建Runtime类示例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();

// 获取Runtime的exec(String cmd)方法
try {
Method runtimeMethod = runtimeClass1.getMethod("exec", String.class);
Process process = (Process) runtimeMethod.invoke(runtimeInstance, "open -a /System/Applications/Calculator.app");
} catch (Exception e) {
System.out.println(e);
}
}
}

c1abfa931867d7ce0f429414ec7f27e0

0x03 相关链接

https://javasec.org/javase/Reflection/Reflection.html
https://www.jianshu.com/p/9be58ee20dee
https://xz.aliyun.com/t/2342