浅谈反射

宋正兵 on 2020-12-09

定义

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。

获取一个类的class对象的三种方式

(1)Object 类支持的 getClass() 方法:public final Class<?>getClass()

Object 类可以根据实例化对象获取 Class 对象,必须是在产生指定类对象后才可以获得。

1
2
3
4
5
6
7
8
class Person {}// 采用自定义的程序类
public class JavaAPIDemo {
public static void main(String[] args) {
Person per = new Person(); // 已经存在有指定类的实例化对象
Class<? extends Person> cls = per.getClass();
System.out.println(cls.getName());// 获取类的完整名称
}
}

(2)JVM 直接支持,采用“类.class”的形式实例化。特点是如果采用此模式,则必须导入程序所对应的开发包。

1
2
3
4
5
6
7
class Person {}// 采用自定义的程序类
public class JavaAPIDemo {
public static void main(String[] args) {
Class<? extends Person> cls = Person.class;
System.out.println(cls.getName());// 获取类的完整名称
}
}

(3)Class 类支持有一个 static 方法,可以加载类:public static Class<?> forName(String className) throws ClassNotFoundException

1
2
3
4
5
6
7
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("cn.my.vo.Person");
System.out.println(cls.getName());// 获取类的完整名称
}
}
// Person类放在cn.my.vo开发包中

通过反射实例化对象

获取 Class 对象的最大意义在于 Class 类里面提供有一个对象的反射实例化方法,它替代了关键字 new:

在 JDK 1.9之前:public T newInstance() throws InstantiationException, IllegalAccessException

观察下面实例可以发现,通过反射实现的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于“类 对象 = new 类()”,相当于隐藏了关键字 new 而直接使用字符串进行了替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* package cn.my.vo中Person.java */
public class Person {
// 任何情况下如果要实例化对象则一定要调用类中的构造方法
public Person() {
System.out.println("*****Person类构造方法*****");
}
@Override
public String toString() {
return "一个Person类";
}
}
/* JavaAPIDemo.java */
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("cn.my.vo.Person");
Object obj = cls.newInstance();// 实例化对象,JDK 1.9后被废除了
System.out.println(obj); // 输出对象调用toString()方法
}
}
/* 执行结果
【cls.newInstance()、关键字new】*****Person类构造方法*****
【System.out.println(obj)】一个Person类
*/

在 JDK 1.9 之后,newInstance() 被替代了:clazz.getDeclaredConstructor().newInstance()

1
2
3
4
5
6
7
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("cn.my.vo.Person");
Object obj = cls.getDeclaredConstructor().newInstance();
System.out.println(obj); // 输出对象调用toString()方法
}
}

反射获取类的基本信息

利用 Class类 可以获取类之中的基本信息,参考反射机制的相关类

应用:反射与简单Java类对象属性的自动设置

在传统的对象实例化并设置数据的操作过程之中,设置数据的部分是最麻烦的,如果类里面提供有大量的属性,那么对程序的赋值来说会出现一堆的setter()方法,会带来大量的重复操作。

反射机制可以根据其自身的特点(Object类直接操作属性或方法)实现相同功能类的重复操作的抽象处理。

属性的自动设置

设计的基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ClassInstanceFactory {
private ClassInstanceFactory() {
}
/**
* 实例化对象的创建方法,该对象可以根据传入的字符串结构“属性:内容|属性:内容”
* @param <T>
* @param clazz 要进行反射实例化Class类对象,有Class就可以反射实例化对象
* @param value 要设置给对象的属性内容
* @return 一个已经配置好属性内容的简单Java类对象
*/
public static <T> T create(Class<?> clazz,String value){
return null;
}
}

单级属性配置

单级属性即所给出的属性数据类型没有其他的引用关系,只描述了本类的对象,单级属性设置需要处理两件事:

  • 通过反射进行指定类对象的实例化处理
  • 进行内容的设置(Field属性类型、方法名称、要设置的内容)

1、定义 StringUtils 实现首字母大写功能

1
2
3
4
5
6
7
8
9
10
11
12
class StringUtils {
public static String initcap(String str) {
if (str == null || "".contentEquals(str)) {
return str;
}
if (str.length() == 1) {
return str.toUpperCase();
} else {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
}

2、定义 BeanUtils 工具类,该工具类主要实现属性的设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BeanUtils { // 进行Bean处理的类
private BeanUtils() {}
/**
* 实现指定对象的属性设置
*
* @param obj 要进行反射操作的实例化对象
* @param value 包含有指定内容的字符串,格式为“属性:内容|属性:内容”
*/
public static void setValue(Object obj, String value) {
String[] results = value.split("\\|"); // 按照“|”进行每一组属性的拆分
for (int x = 0; x < results.length; x++) { // 循环设置属性内容
// attval[0]保存属性名称,attval[1]保存属性内容
String[] attval = results[x].split(":");// 获取属性名称与属性内容
try {
Field field = obj.getClass().getDeclaredField(attval[0]);
Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
field.getType());
setMethod.invoke(obj, attval[1]); // 调用setter方法设置内容
} catch (Exception e) {}
}
}
}

3、ClassInstanceFactory 负责实例化对象并且调用 BeanUtils 类实现属性内容的设置

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
77
78
79
80
81
82
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
String value = "ename:Smith|job:Clerk";
Emp emp = ClassInstanceFactory.create(Emp.class, value);
System.out.println("姓名:" + emp.getEname() + "、职位:" + emp.getJob());
}
}
class ClassInstanceFactory {
private ClassInstanceFactory() {}
/**
* 实例化对象的创建方法,该对象可以根据传入的字符串结构“属性:内容|属性:内容”
*
* @param <T>
* @param clazz 要进行反射实例化Class类对象,有Class就可以反射实例化对象
* @param value 要设置给对象的属性内容
* @return 一个已经配置好属性内容的简单Java类对象
*/
public static <T> T create(Class<?> clazz, String value) {
try {// 如果想采用反射进行简单Java类对象属性设置时,类中必须要有无参构造
Object obj = clazz.getDeclaredConstructor().newInstance();
BeanUtils.setValue(obj, value); // 通过反射设置属性
return (T) obj; // 返回对象
} catch (Exception e) {
e.printStackTrace(); // 如果此时真的出现了错误,本质上抛出异常也无效
return null;
}
}
}
class StringUtils {
public static String initcap(String str) {
if (str == null || "".contentEquals(str)) {
return str;
}
if (str.length() == 1) {
return str.toUpperCase();
} else {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
}
class BeanUtils { // 进行Bean处理的类
private BeanUtils() {}
/**
* 实现指定对象的属性设置
*
* @param obj 要进行反射操作的实例化对象
* @param value 包含有指定内容的字符串,格式为“属性:内容|属性:内容”
*/
public static void setValue(Object obj, String value) {
String[] results = value.split("\\|"); // 按照“|”进行每一组属性的拆分
for (int x = 0; x < results.length; x++) { // 循环设置属性内容
// attval[0]保存属性名称,attval[1]保存属性内容
String[] attval = results[x].split(":");// 获取属性名称与属性内容
try {
Field field = obj.getClass().getDeclaredField(attval[0]);
Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
field.getType());
setMethod.invoke(obj, attval[1]); // 调用setter方法设置内容
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Emp {
private String ename;
private String job;
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}

多种数据类型的属性配置

实际开发之中会面对类中属性类型多样,要实现各种数据类型的配置,在 BeanUtils 类中完成一系列的处理方法。

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
/**
* 实现属性类型转换处理
* @param type 属性类型,通过Field获取的
* @param value 属性的内容,传入的都是字符串,需要将其变为指定类型
* @return 转换后的数据
*/
private static Object convertAttributeValue(String type, String value) {
// System.out.println("type = " + type + "、value = " + value);
if ("long".equals(type) || "Long".equals(type)) { // 长整型
return Long.parseLong(value);
} else if ("int".equals(type) || "java.lang.int".equals(type)) {
return Integer.parseInt(value);
} else if ("double".equals(type) || "java.lang.double".equals(type)) {
return Double.parseDouble(value);
} else if ("java.util.Date".equals(type)) {
SimpleDateFormat sdf = null;
if (value.matches("\\d{4}-\\d{2}-\\d{2}")) { // 日期类型
sdf = new SimpleDateFormat("yyyy-MM-dd");
} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
} else {
return new Date();
}
try {
return sdf.parseObject(value);
} catch (Exception e) {
return new Date(); // 当前日期
}
} else {
return value;
}
}

级联对象实例化

如果现在给定的类对象之中存在有其它的引用的级联关系时,需要多级设置。如一个雇员属于一个部门,一个部门属于一个公司,简单 Java 类的基本定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Company {
private String name;
private Date creatDate;
}
class Dept {
private String dname;
private String loc;
private Company company;
}
class Emp {
private long empno;
private String ename;
private String job;
private double salary;
private Date hiredate;
private Dept dept;
}

如果要对 Emp 进行操作,则应该使用“.”作为级联关系的处理。

1
2
dept.dname:财务部:   		Emp类实例化对象.getDept().setDname("财务部")
dept.company.name:BELTA: Emp类实例化对象.getDept().getCompany().setName("BELTA")

考虑到代码的简洁性,应该通过级联的配置自动实现类中属性的实例化。

1
2
String value = "empno:7369|ename:Smith|job:Clerk|salary:9999|hiredate:1989-10-10"
+ "dept.dname:财务部|dept.company.name:BELTA";

现在的属性存在有多级的关系,那么对于多级的关系就必须与单级的配置区分开。

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class BeanUtils { // 进行Bean处理的类
private BeanUtils() {}
/**
* 实现指定对象的属性设置
* @param obj 要进行反射操作的实例化对象
* @param value 包含有指定内容的字符串,格式为“属性:内容|属性:内容”
*/
public static void setValue(Object obj, String value) {
String[] results = value.split("\\|"); // 按照“|”进行每一组属性的拆分
for (int x = 0; x < results.length; x++) { // 循环设置属性内容
// attval[0]保存属性名称,attval[1]保存属性内容
String[] attval = results[x].split(":");// 获取属性名称与属性内容
try {
if (attval[0].contains(".")) { // 多级配置
String[] temp = attval[0].split("\\.");
Object currentObject = obj;
// 最后一位肯定是指定类中的属性名称,所以不在本次实例化处理范围之内
for (int y = 0; y < temp.length - 1; y++) { // 实例化
// 调用相应的getter方法,如果getter方法返回了null表示该对象未实例化
Method getMethod = currentObject.getClass()
.getDeclaredMethod("get" + StringUtils.initcap(temp[y]));
Object tempObject = getMethod.invoke(currentObject);
if (tempObject == null) { // 该对象现在并没有实例化
Field field = currentObject.getClass().getDeclaredField(temp[y]);// 获取属性类型
Method method = currentObject.getClass()
.getDeclaredMethod("set" + StringUtils.initcap(temp[y]), field.getType());
Object newObject = field.getType().getConstructor().newInstance();
method.invoke(currentObject, newObject);
currentObject = newObject;
} else {
currentObject = tempObject;
}
}
} else {
Field field = obj.getClass().getDeclaredField(attval[0]);
Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
field.getType());
Object convertValue = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
setMethod.invoke(obj, convertValue); // 调用setter方法设置内容
}
} catch (Exception e) {
}
}
}
/**
* 实现属性类型转换处理
* @param type 属性类型,通过Field获取的
* @param value 属性的内容,传入的都是字符串,需要将其变为指定类型
* @return 转换后的数据
*/
private static Object convertAttributeValue(String type, String value) {
if ("long".equals(type) || "Long".equals(type)) { // 长整型
return Long.parseLong(value);
} else if ("int".equals(type) || "java.lang.int".equals(type)) {
return Integer.parseInt(value);
} else if ("double".equals(type) || "java.lang.double".equals(type)) {
return Double.parseDouble(value);
} else if ("java.util.Date".equals(type)) {
SimpleDateFormat sdf = null;
if (value.matches("\\d{4}-\\d{2}-\\d{2}")) { // 日期类型
sdf = new SimpleDateFormat("yyyy-MM-dd");
} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
} else {
return new Date();
}
try {
return sdf.parseObject(value);
} catch (Exception e) {
return new Date(); // 当前日期
}
} else {
return value;
}
}
}

class Company {
private String name;
private Date creatDate;
// 构造、setter、getter
}

class Dept {
private String dname;
private String loc;
private Company company;
// 构造、setter、getter
}

class Emp {
private long empno;
private String ename;
private String job;
private double salary;
private Date hiredate;
private Dept dept;
// 构造、setter、getter
}

级联属性设置

现在已经成功的实现级联的对象实例化处理,随后需要考虑级联的属性设置。

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
String value = "empno:7369|ename:Smith|job:Clerk|salary:9999|hiredate:1989-10-10|"
+ "dept.dname:财务部|dept.company.name:BELTA";
Emp emp = ClassInstanceFactory.create(Emp.class, value);
System.out.println("员工编号:" + emp.getEmpno() + "姓名:" + emp.getEname() + "、职位:" + emp.getJob() + "、工资"
+ emp.getSalary() + "、入职日期" + emp.getHiredate());
System.out.println(emp.getDept().getDname());
System.out.println(emp.getDept().getCompany().getName());
}
}
class ClassInstanceFactory {
private ClassInstanceFactory() {
}
/**
* 实例化对象的创建方法,该对象可以根据传入的字符串结构“属性:内容|属性:内容”
* @param <T>
* @param clazz 要进行反射实例化Class类对象,有Class就可以反射实例化对象
* @param value 要设置给对象的属性内容
* @return 一个已经配置好属性内容的简单Java类对象
*/
public static <T> T create(Class<?> clazz, String value) {
try {// 如果想采用反射进行简单Java类对象属性设置时,类中必须要有无参构造
Object obj = clazz.getDeclaredConstructor().newInstance();
BeanUtils.setValue(obj, value); // 通过反射设置属性
return (T) obj; // 返回对象
} catch (Exception e) {
e.printStackTrace(); // 如果此时真的出现了错误,本质上抛出异常也无效
return null;
}
}
}
class StringUtils {
public static String initcap(String str) {
if (str == null || "".contentEquals(str)) {
return str;
}
if (str.length() == 1) {
return str.toUpperCase();
} else {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
}
class BeanUtils { // 进行Bean处理的类
private BeanUtils() {
}
/**
* 实现指定对象的属性设置
* @param obj 要进行反射操作的实例化对象
* @param value 包含有指定内容的字符串,格式为“属性:内容|属性:内容”
*/
public static void setValue(Object obj, String value) {
String[] results = value.split("\\|"); // 按照“|”进行每一组属性的拆分
for (int x = 0; x < results.length; x++) { // 循环设置属性内容
// attval[0]保存属性名称,attval[1]保存属性内容
String[] attval = results[x].split(":");// 获取属性名称与属性内容
try {
if (attval[0].contains(".")) { // 多级配置
String[] temp = attval[0].split("\\.");
Object currentObject = obj;
// 最后一位肯定是指定类中的属性名称,所以不在本次实例化处理范围之内
for (int y = 0; y < temp.length - 1; y++) { // 实例化
// 调用相应的getter方法,如果getter方法返回了null表示该对象未实例化
Method getMethod = currentObject.getClass()
.getDeclaredMethod("get" + StringUtils.initcap(temp[y]));
Object tempObject = getMethod.invoke(currentObject);
if (tempObject == null) { // 该对象现在并没有实例化
Field field = currentObject.getClass().getDeclaredField(temp[y]);// 获取属性类型
Method method = currentObject.getClass()
.getDeclaredMethod("set" + StringUtils.initcap(temp[y]), field.getType());
Object newObject = field.getType().getConstructor().newInstance();
method.invoke(currentObject, newObject);
currentObject = newObject;
} else {
currentObject = tempObject;
}
}
// 进行属性内容设置
Field field = currentObject.getClass().getDeclaredField(temp[temp.length - 1]); // 获取成员
Method setMethod = currentObject.getClass()
.getDeclaredMethod("set" + StringUtils.initcap(temp[temp.length - 1]), field.getType());
Object convertValue = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
setMethod.invoke(currentObject, convertValue); // 调用setter方法设置内容
} else {
Field field = obj.getClass().getDeclaredField(attval[0]);
Method setMethod = obj.getClass().getDeclaredMethod("set" + StringUtils.initcap(attval[0]),
field.getType());
Object convertValue = BeanUtils.convertAttributeValue(field.getType().getName(), attval[1]);
setMethod.invoke(obj, convertValue); // 调用setter方法设置内容
}
} catch (Exception e) {
}
}
}
/**
* 实现属性类型转换处理
* @param type 属性类型,通过Field获取的
* @param value 属性的内容,传入的都是字符串,需要将其变为指定类型
* @return 转换后的数据
*/
private static Object convertAttributeValue(String type, String value) {
if ("long".equals(type) || "Long".equals(type)) { // 长整型
return Long.parseLong(value);
} else if ("int".equals(type) || "java.lang.int".equals(type)) {
return Integer.parseInt(value);
} else if ("double".equals(type) || "java.lang.double".equals(type)) {
return Double.parseDouble(value);
} else if ("java.util.Date".equals(type)) {
SimpleDateFormat sdf = null;
if (value.matches("\\d{4}-\\d{2}-\\d{2}")) { // 日期类型
sdf = new SimpleDateFormat("yyyy-MM-dd");
} else if (value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
} else {
return new Date();
}
try {
return sdf.parseObject(value);
} catch (Exception e) {
return new Date(); // 当前日期
}
} else {
return value;
}
}
}

class Company {
private String name;
private Date creatDate;
// 构造、setter、getter
}

class Dept {
private String dname;
private String loc;
private Company company;
// 构造、setter、getter
}

class Emp {
private long empno;
private String ename;
private String job;
private double salary;
private Date hiredate;
private Dept dept;
// 构造、setter、getter
}