Java反射机制

了解Java反射机制前,先来看下Java怎么运行程序的?

通常我们知道先运行javac xxx.java再运行java xxx.class,对应起来就是编译生成.class字节码文件,再将.class文件通过JVM的类加载器加载到内存进行运行,所以Java运行分为两种状态编译时和运行时,而现在学习的Java反射机制是在运行时提供的功能。

0x01 什么是Java反射机制

先来看下百度百科给出的概念:

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射(Reflection)被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

这里插入一张比较容易理解的图:

image-20200605144917651

简单来说 就是Java程序在运行时 还允许你通过反射机制获取某个对象的类,还能构造对象、获取对象属性、方法并且能调用方法。看到这里我大概能想到为什么反序列化远程代码执行漏洞能够存在了,在程序运行中,反序列化为一个对象,利用某个或某些类内部复杂的关系调用到反射机制然后执行某个运行命令的方法,从而完成命令执行。

0x02 利用反射机制能做什么

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法;
  4. 在运行时调用任意一个对象的成员变量和方法;
  5. 生成动态代理。

1-4刚刚概念里面都提到了,5动态代理没有提到,后面文章会着重学习下。

0x03 在Java中如何实现反射

关于Java反射机制主要有以下几个API

1
2
3
4
5
java.lang.Class; //类               
java.lang.reflect.Constructor;//构造方法
java.lang.reflect.Field; //类的成员变量
java.lang.reflect.Method;//类的方法
java.lang.reflect.Modifier;//访问权限,诸如public, static等

java.lang.Class

对应可以实现 2.1在运行时判断任意一个对象所属的类

在Object类中定义了一个方法,此方法将被所有子类继承:public final Class getClass();返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

通过四种方法可以获取class对象

  • 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高:

    1
    Class<Person> clazz = Person.class
  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象;

    1
    2
    Person person = new Person();
    Class clazz = person.getClass();
  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出异常ClassNotFoundException

    1
    2
    3
    4
    5
    try {
    class1 = Class.forName("xxx.Person");
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
  • 通过类加载器来获取

    1
    2
    ClassLoader clazz = this.getClass().getClassLoader();
    Class clazz = clazz.loadClass(“类的全类名”);

java.lang.reflect.Constructor

对应实现 2.2在运行时构造任意一个类的对象。

class对象动态生成的方法:

  1. 调用Class对象的new instance() 方法来实例化,注意这种方法只能调用无参构造器:Object obj = class1.newInstance();
  2. 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
1
2
Constructor<?> constructor = class1.getDeclaredConstructor(new Class[]{String.class});//获取指定声明构造函数。指定new Class[]{String.class}设置传参的类
obj = constructor.newInstance(new Object[]{"lcj"});

java.lang.reflect.Field 和 java.lang.reflect.Method;

对应实现 2.3在运行时判断任意一个类所具有的成员变量和方法和2.4在运行时调用任意一个对象的成员变量和方法。

getFields()和 getMethods()用来获取类的成员变量和方法

Object invoke(Object obj, Object[] args)调用类的方法,并向方法中传递要设置的obj对象及其方法需要参数信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性
Field[] publicFields = class1.getFields();//获取class对象的public属性
try {
Field ageField = class1.getDeclaredField("age");//获取class指定属性
Field desField = class1.getField("des");//获取class指定的public属性
} catch (NoSuchFieldException e) {
e.printStackTrace();
}

Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法
Method[] allMethods = class1.getMethods();//获取class对象的所有方法 包括父类的方法

getAgeMethod = getDeclaredMethod("getAge");
Object result = getAgeMethod.invoke(obj, new Class[]{});//调用方法

0x04 示例

先定义一个类TestClass

TestClass
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestClass {
public String a = "adf";
private String b;

public void method(String v) {
System.out.println("正在执行method方法...");
System.out.println(v);
}

private String getB(){
return this.b;
}
}

ReflectTest可以通过反射机制获取类名、类的属性和方法、实例化类并执行方法

ReflectTest.java
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
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
ReflectTest reflectTest = new ReflectTest();
reflectTest.getAllFiled();
reflectTest.getAllMethod();

//实例化类并执行方法
Class<?> clazz = Class.forName("r17a.Learning0905.ReflectTest.TestClass");
Object object = clazz.newInstance();
Method method = clazz.getDeclaredMethod("method", String.class);
method.invoke(object,"这是我的输入");
}

public void getAllFiled() {
//获取类
Class testClass = TestClass.class;
System.out.println("类的名称:" + testClass.getName());

//获取所有 public 访问权限的变量
// Field[] fields = testClass.getFields();

// 获取所有本类声明的变量(不问访问权限)
Field[] fields = testClass.getDeclaredFields();
//遍历变量并输出变量信息
for (Field field : fields) {
//获取访问权限并输出
int modifiers = field.getModifiers();
System.out.print(Modifier.toString(modifiers) + " ");
//输出变量的类型及变量名
System.out.println(field.getType().getName() + " " + field.getName());
}
}

public void getAllMethod() {
//获取类
Class testClass = TestClass.class;
// 获取类的所有方法
Method[] methods = testClass.getDeclaredMethods();
for (Method method : methods) {
//获取访问权限并输出
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + " ");
//输出变量的类型及变量名
System.out.println(method.getReturnType() + " " + method.getName());
}
}
}

执行结果:

image-20200904213632298

参考链接:

https://baijiahao.baidu.com/s?id=1645023991123759354&wfr=spider&for=pc

https://www.jianshu.com/p/2d685da7ed74

https://www.cnblogs.com/whoislcj/p/6038511.html

https://blog.csdn.net/chengzhang1989/article/details/70216634/