0%

反射

https://www.bilibili.com/video/BV1ke4y1w7yn

什么是反射 ?

反射允许对字段(成员变量),成员方法和构造方法的信息进程编程访问。

获取类对象字节码文件的三种方式

  • Class.forName(“全类名”);

  • 类名.class;

  • 对象.getClass();

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
package com.qust;

import com.qust.reflect.Student;

public class Main {
public static void main(String[] args) {

/**
* 获取 class 对象的三种方式
*/

Class clazz1 = null;
try {
clazz1 = Class.forName("com.qust.reflect.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

Class clazz2 = Student.class;

Class clazz3 = (new Student()).getClass();

System.out.println(clazz1);

System.out.println(clazz2);

System.out.println(clazz3);

}
}

点击类名,点击 copy - cpoy as Reference,即可复制全类名(包名 + 类名)

利用反射获取成员变量信息

Class 中用于获取成员变量的方法

  • Field[] getFields() : 返回所有公共成员变量对象的数组

  • Field[] getDeclaredFields(): 返回所有成员变量对象的数组

  • Field getField() : 返回单个公共成员变量对象

  • Field getDeclaredField() : 返回单个成员变量对象

Field 类中用于创建对象的方法

  • void set(Object obj, Object value) : 赋值

  • Object get(Object obj) :获取值

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.qust;

import com.qust.reflect_.Student;

import java.lang.reflect.Field;

public class Main {
public static void main(String[] args) {
// 获取字节码文件
Class clazz;
try {
clazz = Class.forName("com.qust.reflect_.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

// 获取所有公共成员变量
Field[] fields = clazz.getFields();
System.out.println("All public variable:");
for(Field f : fields) {
System.out.println(f.getName());
}
System.out.println();

// 获取所有成员变量
fields = clazz.getDeclaredFields();
System.out.println("All variable:");
for(Field f : fields) {
System.out.println(f.getName());
}
System.out.println();

// 获取单个成员变量
Field gender = null;
try {
gender = clazz.getField("gender");
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
System.out.println(gender);
System.out.println();

Field name = null;
try {
name = clazz.getDeclaredField("name");
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
System.out.println(name);
System.out.println();

// 获取权限修饰符
System.out.println(name.getModifiers()); // 2 : 私有的
// 获取成员变量名
System.out.println(name.getName());
// 获取数据类型
System.out.println(name.getType());
// 获取对象的值
Student student = new Student(11, "wpc", "male");
name.setAccessible(true);
Object value = null;
try {
value = name.get(student);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
System.out.println(value);
// 修改对象值
try {
name.set(student,"wpc_");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
System.out.println(student);
}
}

利用反射获取成员方法

Class 类中获取成员方法的方法

  • Method[] getMethods() : 返回所有公共成员方法的数组,包括继承的

  • Method[] getDeclaredMethods()

  • Methods[] getMethod(String name, Class<?> … Parameters)

  • Methods[] getDeclaredMethod(String name, Class<?> … Parameters)

Method 类中用于创建对象的方法

  • Object invoke(Object object, Object .. args) : 运行方法

参数一 :用 obj 对象调用该方法

参数二 :调用方法的传递的参数(没有就不写)

返回值 :方法的返回值(没有就不写)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.qust;

import com.qust.reflect__.Student;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
public static void main(String[] args) {
Class clazz;
try {
clazz = Class.forName("com.qust.reflect__.Student");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

// 获取所有的方法对象(包括父类的所有公共方法)
// Method[] methods = clazz.getMethods();
// for(Method method : methods) {
// System.out.println(method.getName());
// }


// 获取所有方法(不包括父类)
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
System.out.println(method.getName());
}

// 获取指定的单一方法
Method m = null;
try {
m = clazz.getMethod("eat", String.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
System.out.println(m);

// 获取方法的修饰符
int modifiers = m.getModifiers();
// 获取方法名字
String name = m.getName();
// 方法形参
m.getParameters();
m.getParameterCount();
m.getParameterTypes();

// 方法抛出异常
Class[] exps = m.getExceptionTypes();


/**
* 运行方法 invoke
* 参数一 : 方法的调用者
* 参数二 : 调用方法传递的参数
*/
try {
String res = (String) m.invoke(new Student(), "shit");
System.out.println(res);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}

// 获取方法返回值
Class<?> returnType = m.getReturnType();
System.out.println(returnType);
}
}

反射的作用

  • 获取一个类的所有的信息,获取到了之后,在执行其他的业务逻辑

  • 结合配置文件,动态的创建并调用方法

练习一 保存信息

对于任意一个对象,都可以把对象的所有字段名和值,保存到文件中

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
package com.qust.practice1;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;

public class MyReflectDemo {
public static void main(String[] args) throws IOException, IllegalAccessException {
Student s = new Student("wpc", 21, "male", "learn", 180.5);
Teacher t = new Teacher("teacher1", -100);
System.out.println(System.getProperty("user.dir"));
saveObjects(s);
}

// 将对象里面所有的成员变量名和值保存到本地文件中
public static void saveObjects(Object obj) throws IOException, IllegalAccessException {
// 1. 获取字节码文件对象
Class clazz = obj.getClass();

// 2. 创建 IO 流
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\95432\\Desktop\\Java-Reflect\\demo-reflect\\src\\main\\java\\com\\qust\\practice1\\data.txt"));
// BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt"));

// 3. 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
Object value = field.get(obj);
System.out.println(name + "=" + value);
bw.write(name + "=" + value);
bw.write("\n");
}
bw.close();
}
}

不知道为什么相对路径写入信息写不进去,也没提示找不到文件报错

练习二

跟配置文件结合,动态创建对象,并调用方法

  • Main.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
package com.qust.practice2;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 读取配置文件信息
Properties prop = new Properties();
prop.load(new FileInputStream("C:\\Users\\95432\\Desktop\\Java-Reflect\\demo-reflect\\src\\main\\resources\\test .properties"));
System.out.println(prop);

// 获取全类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");

System.out.println(className);
System.out.println(methodName);

// 利用反射创建对象,并运行方法
Class clazz = Class.forName(className);
// 获取构造方法
Constructor con = clazz.getDeclaredConstructor();
Object o = con.newInstance();
System.out.println(o);

// clazz.newInstance(); @deprecated

clazz.getDeclaredMethod(methodName).invoke(o);
}
}
  • test.properties
1
2
classname=com.qust.practice2.Student
method=learn

如果之后要运行别的类的方法,修改配置文件即可,例如修改为

className

classname=com.qust.practice2.Teacher
method=teach