Java反射机制深入学习

Java反射

反射机制

Java的反射机制是指在运行状态中,对于任何一个类都能知道其所有的属性和方法,并且对任意一个对象都能调用他的任意方法,这种动态获取信息以及调用对象的方法称为Java的反射机制。

反射机制是java反序列化的重中之重,Java安全可以从反序列化漏洞开始说起,反序列化漏洞⼜可以从反射开始说起。万物有阴就有阳,有反射肯定也会有“正射”,正射就是我们正常通过new去实例化类,然后用实例化好的对象进行操作。反射可以获取对象的类,类可以通过反射获取所有方法,通过反射修改属性和调用方法。

通过反射获取类的三种方式:

  • 对象.getClass();

  • Class.forName(包名+类名);会加载类

  • 类名.class 不会加载类

代码还是用presion类,然后新建一个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


public class RefeltionTest {
public static void main (String[] args) throws Exception {
Presion p = new Presion();
System.out.println(p);

}
}

先来看getClass()跟进方法看

返回该Object的运行时间类,被final修饰,会返回类型类,类型类指的是代表一个类型的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


public class RefeltionTest {
public static void main (String[] args) throws Exception {
Presion p = new Presion();
// System.out.println(p);
Class c = p.getClass();
System.out.println(c);
}
}

返回了对象p的类

我们在定义一个类的时候会用到一个class的关键字,这个关键字和Class的关系就是 写完一个类编译保存到硬盘里面时会有一个presion.class对象,定义的类名称不同 .class文件名也不同,此时的这个Class对应的就是这个.class对象,包含了与类有关的信息。

跟进去看之后发现这个Class是实现了序列化接口的,Class里面还有一个重要的方法forName();

forName的参数是类名,返回你传入类名的原型类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.lang.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


public class RefeltionTest {
public static void main (String[] args) throws Exception {
Presion p = new Presion();
// System.out.println(p);
// Class c = p.getClass();
Class c = Class.forName("Presion");
System.out.println(c);
}
}

第三种就是使用.class方法获取类

通过反射创建对象的两种方式:

  • Class对象的newInstance()方法; 默认调用无参构造器
  • 若没有无参构造器,则先getConstructor(Class…args);,con.newInstance(Object…args);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


public class RefeltionTest {
public static void main (String[] args) throws Exception {
Presion p = new Presion();
// System.out.println(p);
// Class c = p.getClass();
// Class c = Class.forName("Presion");
// System.out.println(c);
Constructor constructor = c.getConstructor(String.class,int.class);
Presion presion = (Presion) constructor.newInstance("ly0n",21);
System.out.println(presion);
}
}

为什么传入的是String.class,int.class可以跟进getConstructor看,他的参数就是Class<?>,跟进方法去看代码里面有注释可以理解然后对照一下就明白了

代码执行完后就会生成一个对象

通过反射获得及修改对象的属性值:

  • Class对象getField(String 属性名)方法
  • f.get(Object obj) 获取具体对象的属性值
  • f.set(Object obj,value) 更改属性

加了s也就是复数的意思,然后类型就变成数组,就是获取对象所有的属性值单不包括私有属性。

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.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


public class RefeltionTest {
public static void main (String[] args) throws Exception {
Presion p = new Presion();
// System.out.println(p);
Class c = p.getClass();
// Class c = Class.forName("Presion");
// Class c = Presion.class;
// System.out.println(c);

// Constructor constructor = c.getConstructor(String.class,int.class);
// Presion presion = (Presion) constructor.newInstance("ly0n",21);
// System.out.println(presion);
//获取类的属性
//通过数组获取
Field[] presionfields = c.getFields();
// Field[] presionfields = c.getDeclaredFields();
for(Field f:presionfields){
System.out.println(f);
}
}
}

得到的结果

在presion中是有两个属性的

这也就是刚刚上面说的不包括私有属性的原因,要获取私有属性就需要getDeclaredFields方法

更改属性的值具体代码实现

1
2
3
4
5
6
Field namefield2 = c.getField("name");
namefield2.set(presion,"lsdasd");
Field namefield1 = c.getDeclaredField("age");
namefield1.setAccessible(true);
namefield1.set(presion,20);
System.out.println(presion);

通过反射调用方法

  • Class对象getMethod(String 方法名,Class…args);
  • m.invoke(Object 对象名,Object…args);

使用getMethod方法和使用getField方法大同小异

尝试获取类里面的方法并打印出来

1
2
3
4
Method[] getMethod = c.getMethods();
for(Method m:getMethod){
System.out.println(m);
}

包含了挺多继承下来的方法。也包括自己写的一个方法

使用getMethod去获取单个方法,需要注意的点是getMethod的参数是两个一个是方法名,一个是Class类

1
2
Method getMethod = c.getMethod("action", String.class);
getMethod.invoke(presion,"asdasdasd");

成功调用了action方法