彻底搞懂Java反射Reflect

背景知识

在了解反射之前,我们首先了解一下Class和Object:

  • Class: 所有类的根源,是整个Java反射机制的源头。
  • Object:所有对象的根源。

Class类

Class类的作用

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 生成动态代理

Class类的使用

本次通过代码+注释来驱动讲解Java的反射知识,这样比纯文字描述了解的要快而且记得牢的多:

首先,创建一个类AkaThinkBook.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
package com.akathink.reflect;
/**
*
* 本类提供了两个字段、两个构造方法、五个普通方法,供验证反射机制使用
*
*/
public class AkaThinkBook {
private String bookName;
private String bookAuthor;
public AkaThinkBook() {
super();
}
public AkaThinkBook(String bookName, String bookAuthor) {
super();
this.bookName = bookName;
this.bookAuthor = bookAuthor;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public void getBookInfo(){
System.out.println("AkaThink即将出版的《音画》APP为您提供优质的音画作品,带给您美的享受!");
}
}

其次,定义一个ClassUtil.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
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
package com.akathink.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtil {
/**
* 打印对象的字段信息
*
* @param object
*/
public static void printFieldMsg(Object object) {
System.out.println("\n\n下面打印的是对象的 字段 信息:\n");
Class clazz = object.getClass();
/**
* 成员变量也是对象
*
* java.lang.reflect.Field
*
* Field类封装了关于成员变量的操作
*
* getFields()获取的是所有的Public的成员变量的信息
*
* getDeclaredFields()获取的是该类自己声明的变量的信息
*
*/
// Field[] fields = clazz.getFields();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
// 得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
/**
* 打印对象的构造函数的信息
*
* @param object
*/
public static void printConstructorMsg(Object object) {
System.out.println("\n下面打印的是对象的 构造方法 信息:\n");
Class clazz = object.getClass();
/**
* 构造函数也是对象
*
* java.lang.reflect.Constructor
*
* getConstructors()获取的是该类的所有public方法
*
* getDeclaredConstructors()获取的是该对象自己声明的构造方法
*/
// Constructor[] constructors = clazz.getConstructors();
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.print(constructor.getName() + "(");
Class[] paramTypes = constructor.getParameterTypes();
for (Class paramClazz : paramTypes) {
System.out.print(paramClazz.getName()+ " , " );
}
System.out.println(")");
}
}
/**
* 打印对象的方法信息
*
* @param object
*/
public static void printMethodMsg(Object object) {
System.out.println("\n下面打印的是对象的 方法 信息:\n");
// 获取该对象的类类型
Class clazz = object.getClass();
System.out.println("类的名称是:" + clazz.getName());
/**
* Method类,方法对象
*
* java.lang.reflect.Method
*
* 一个成员方法就是一个Method对象
*
* getMethods()方法获取的就是该class or interface 的所有的public的方法,包括父类继承的方法。
*
* getDeclaredMethods()获取的是该class or interface 自己声明的方法,不问访问权限。
*/
// Method[] methods = clazz.getMethods();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 得到方法的返回值类型的类类型
Class returnType = method.getReturnType();
System.out.print(returnType.getName() + " ");
// 得到方法的名称
System.out.print(method.getName() + "(");
// 获取参数类型--> 得到的是参数列表的类类型
Class[] paramTypes = method.getParameterTypes();
for (Class paramClazz : paramTypes) {
System.out.print(paramClazz.getName() + " , ");
}
System.out.println(")");
}
}
}

最后,定义一个:ClassDemo.java 来帮助我们了解Class类的使用

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.akathink.reflect;
/**
*
*Class类的使用
*
*/
public class ClassDemo {
public static void main(String[] args) {
/**
* AkaThinkBook也是一个实例对象
* 任何一个类都是Class的实例对象,这个实例对象有三种表示方式
*
*/
AkaThinkBook akaThinkBook = new AkaThinkBook();
/**
* 第一种表示方式-->实际在告诉我们任何一个类都有一个隐含的静态成员变量class
*/
Class clazz1 = AkaThinkBook.class;
/**
* 第二种表示方式-->已经知道该类的对象,通过getClass()获取
*/
Class clazz2 = akaThinkBook.getClass();
/**
*
* 总结:
*
* clazz1、clazz2在官网里称为AkaThinkBook的类类型
*
* 万事万物皆对象
*
* 类也是对象,是Class类的实例对象
*
* 这个对象我们称之为该类的类类型
*
*
*/
/**
* 一个类只可能是Class类的一个实例对象,无论采用哪种方式获取返回的都是同一个对象。
*/
System.out.println(clazz1 == clazz2);
/**
* 第三种表示方式-->不仅表示类的类类型,还代表了动态加载类
*
* 编译时刻加载类是静态加载类,运行时刻加载类是动态加载
*
*/
Class clazz3 = null;
try {
clazz3 = Class.forName("com.akathink.reflect.AkaThinkBook");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(clazz1 == clazz3);
try {
//我们完全可以通过类的类类型创建该类的实例对象
AkaThinkBook book = (AkaThinkBook)clazz1.newInstance();//需要有无参数的构造方法
book.getBookInfo();
} catch (Exception e) {
e.printStackTrace();
}
ClassUtil.printFieldMsg(akaThinkBook);
ClassUtil.printConstructorMsg(akaThinkBook);
ClassUtil.printMethodMsg(akaThinkBook);
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
true
true
AkaThink即将出版的《音画》APP为您提供优质的音画作品,带给您美的享受!
下面打印的是对象的 字段 信息:
java.lang.String bookName
java.lang.String bookAuthor
下面打印的是对象的 构造方法 信息:
com.akathink.reflect.AkaThinkBook()
com.akathink.reflect.AkaThinkBook(java.lang.String , java.lang.String , )
下面打印的是对象的 方法 信息:
类的名称是:com.akathink.reflect.AkaThinkBook
void getBookInfo()
java.lang.String getBookName()
void setBookName(java.lang.String , )
java.lang.String getBookAuthor()
void setBookAuthor(java.lang.String , )

方法反射的操作

通过上面的案例,我们可以很方便的获取都对象里面的任何字段、方法、构造方法等信息,下面我们再讲解一下如何通过反射调用方法:
下面是通过反射调用方法的模板:

1
method.invoke(对象,参数列表)

我们创建一个:MethodDemo.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
package com.akathink.reflect;
import java.lang.reflect.Method;
public class MethodDemo {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
AkaThinkBook akaThinkBook = new AkaThinkBook("魔女闹江湖", "顾漫");
// 第一步:获取类类型
Class clazz = akaThinkBook.getClass();
System.out.println("未修改之前书名为:" + akaThinkBook.getBookName());
// 第二步:获取方法
Method method;
try {
method = clazz.getMethod("setBookName", String.class);
// 第三步:调用setBookName(String bookName)方法
method.invoke(akaThinkBook, "微微一笑很倾城");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("修改之后书名为:" + akaThinkBook.getBookName());
}
}

运行结果

1
2
未修改之前书名为:魔女闹江湖
修改之后书名为:微微一笑很倾城

通过Class、Method来认识泛型的本质

创建一个ArrayListDemo.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 com.akathink.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList mDataList1 = new ArrayList();
/**
*
* 我曾经见过的一个面试题:如何让ArrayList<String>类型的变量添加一个int类型的值,下面就以此为例来介绍如何通过反射了解集合泛型的本质
*
*
*/
ArrayList<String> mDataList2 = new ArrayList<String>();
System.out.println(mDataList1.getClass() == mDataList2.getClass());
/**
*
*
* 返回true说明编译之后集合的泛型是去泛型话的
*
* Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
*
* 反射的操作都是编译之后的操作
*
* 验证:我们可以通过方法的反射来操作,绕过编译
*
*
*/
try {
Method m = mDataList2.getClass().getMethod("add", Object.class);
m.invoke(mDataList2, 10);
m.invoke(mDataList2, 20);
m.invoke(mDataList2, 30);
System.out.println(mDataList2);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}

运行结果

1
2
true
[10, 20, 30]

参考资料

http://www.imooc.com/learn/199
http://www.cnblogs.com/yaozhongxiao/archive/2013/05/21/3091353.html