这里话是看这篇文章来进行学习的

image-20230314221340100

上面的就是java反射的作用

这里的我们在本地自己写一个类来进行测试

Person.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
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Person implements Serializable{
private String name;
private int age;
public Person()
{
//构造函数
}
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
@Override //对Serializable这个接口里的toString函数进行重写
public String toString()
{
return "Person{" +
"name='" + name + '\'' +
",age=" + age +
'}';
}
private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException
{
//重新对readObject函数进行重写,然后在里面加一条命令执行的语句,这样的话就会在反序列化的时候就会执行,这样的话就会造成安全问题
ois.defaultReadObject();;
Runtime.getRuntime().exec("calc"); //Runtime.getRuntime().exec 用于调用外部可执行程序或系统命令
}

}

然后在定义一个reflect类来测试,就是在这个reflect类里边进行反射调试

image-20230314222547749

在这个类里边,getClass()是得到person对象的Class

反射的实质就是操控Class

image-20230314223516379

这里从class里面实例化对象,方便含有无参和有参的方法

里面的直接使用c.newInstance();是调用Person类里边的无参构造函数

使用下面这个代码的话是调用Person类里边的有参构造方法,在c.getConstructor(String.class,int.class);里边的参数,是根据Person类里边设置好的参数来进行填写,因为Person类里边的构造函数是(string,int),

所以这里就这样填c.getConstructor(String.class,int.class);

最后直接在Person person1 =(Person) constructor.newInstance("tom",12);里边输入值,然后使用S.o.p输出就行了

1
2
Constructor constructor = c.getConstructor(String.class,int.class);
Person person1 =(Person) constructor.newInstance("tom",12);

image-20230314224245408

获取类里面属性

image-20230315165736464

person类里边只有两个属性,一个公开的name,和一个私有的age

image-20230315165828360

image-20230315170002571

getField()方法就是获取属性的方法,属于Field这个类,但是getField()方法只能获取公开属性的方法,私有属性的使用另一个方法,下面会继续讲

这里代码的意思是,先获取person类里边的属性用数组装起来,然后用for循环进行输出

image-20230315170301659

image-20230315170308038

getDeclaredFields();方法就是可以访问私有和公有属性的,其他函数种只要含有Declared的,都是可以访问私有的

可以获取类里边的属性了,那么我们就可以尝试修改一下这些属性的值了

image-20230315170925185

image-20230315170937920

这就是修改前后的样子

代码是先获取到person类里边的属性,并确定要修改哪一个属性,这里写的是getField(),没有在末尾加上s,那么没加s的就是可以进行传参来确定要修改的值的,这里传的参数是name,那么我们就可以进行name属性值的修改,然后使用set()方法来确定修改谁的,修改成啥内容,这里添加的对象必须是实例化后的对像,所以是person1

image-20230315171418567

上面的代码是修改不了私有属性的

image-20230315171806679

image-20230315171818318

成功进行修改,这里修改的关键就是这两行代码

1
2
Field field = c.getDeclaredField("age");
field.setAccessible(true);

其他代码的解释和上面说的一样

接下来是调用类里边的方法

image-20230315173404879

image-20230315173513046

还是和上面那个获取属性的方法一样,使用末尾加s的函数,利用数组来存贮,然后使用for循环输出

他这里获取的方法有person里边的,还有Object对象的,因为所有的类都继承于他,他是个底层类

接下来我们换一种写法,不使用数组的写法

image-20230315174116598

image-20230315174128040

就是这两个简单的代码,然后就可以访问person类里边的函数方法了,这里的getMethod()方法和那个getConstructor()方法类似,都是必须得告诉一下我们使用的是啥类型的参数。这里的invoke()方法在这里就是触发的意思,就是触发person里边的action方法,这里传的参数和上面的set()是一个意思,就是第一位得传实例化的对象,然后第二位传参数(可以在输出函数名的时候进行查看,idea会告诉你该传什么参数)

接下来就是调用私有方法

image-20230315175014198

这里话是把getMethod换成了getDeclaredMethod,然后加一个可以进行修改的权限,method.setAccessible(true);

reflect.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
48
49
package org.example;

import com.sun.xml.internal.ws.api.model.MEP;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class reflect {

public static void main(String[] args) throws Exception
{
Person person = new Person();
Class c = person.getClass();
//从class里面实例化对象
c.newInstance(); //这个是只能调用person类里边的无参构造函数

Constructor constructor = c.getConstructor(String.class,int.class);
Person person1 =(Person) constructor.newInstance("tom",12);
System.out.println(person1);

// //获取类里边的属性
// Field[] fields = c.getDeclaredFields();
// for (Field f:fields)
// System.out.println(f);
Field field = c.getDeclaredField("age");
field.setAccessible(true);
field.set(person1,100);
System.out.println(person1);


//调用类里边的方法
// Method[] method = c.getMethods();
// for (Method m:method)
// {
// System.out.println(m);
// }
// Method method = c.getMethod("action", String.class);
// method.invoke(person1,"test");


Method method = c.getDeclaredMethod("action", String.class);
method.setAccessible(true);
method.invoke(person1,"test");

}

}

以上就是全部java反射的学习过程了