Java 反射机制

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

动态:运行期间动态绑定执行规则。

静态:编译以后就已经确定的执行过程。

动态加载类到方法区

1
2
3
4
5
6
7
8
9
10
11
public void test1(){
String str = new String("Hello");
Class cls = String.class;
//动态获取 String类型的方法信息:
Method[] all = cls.getDeclaredMethods();
//Method 代表方法的信息
// method.getName 可以获取方法的名称
for (Method method : all) {
System.out.println(method.getName());
}
}

创建对象过程

1.Java 将类加载到方法区(自动完成)

2.利用类创建对象。内存中有一个String对象,方法区中还有类的信息!通过Class对象可以获取类的相关信息。

54136091-file_1487994424894_c44a.png

动态获取类的信息

动态获取类的方法信息

Foo类(package:demo):

1
2
3
4
5
6
7
8
9
10
//编译以后是 demo.Foo.class
//类的全名是 demo.Foo
class Foo{
public int test(){
return 5;
}
public double test1(){
return 5d;
}
}

动态获取Foo类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testClassForName(){
/*
* 动态的加载类信息到方法区
* 并且返回对应的Class对象!
* Class 对象可以访问类的全部信息!
*
* 将className对应的类文件,从磁盘中加载
* 内存方法区,返回这个类的信息
*/
String className = "demo.Foo";
try {
Class cls = Class.forName(className);
Method[] all = cls.getDeclaredMethods();
for (Method method : all) {
System.out.println(method.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

输出:

1
2
test
test1​

动态获取类的属性信息

Eoo类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Eoo{
int id;
String name;
double salary;
String meno;
public Eoo(){

}
public Eoo(int id, String name, double salary, String meno) {
super();
this.id = id;
this.name = name;
this.salary = salary;
this.meno = meno;
}
// get,set略
}

动态获取Eoo类的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testField()throws Exception {
/*
* 动态获取一个类的全部属性信息
* 1 动态加载一个类到方法区
* 2 动态获取类的属性信息
*/
String className = "demo.Eoo";
//动态加载类
Class cls = Class.forName(className);
//动态获取类声明的属性信息
Field[] all = cls.getDeclaredFields();
for (Field field : all) {
//getName 获取属性的名字
System.out.print(field.getName()+" ");
}
}

输出:

1
id name salary meno

动态获取类的构造器信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testCon() throws Exception{
/*
* 1 动态加载类
*/
String className = "demo.Eoo";
Class cls = Class.forName(className);

Constructor[] all = cls.getDeclaredConstructors();
for (Constructor c : all) {
System.out.print(c.getName());
//获取构造器的参数类型列表
// Parameter 参数 Type类型
// Class[] 代表所有参数的类型列表
Class[] types = c.getParameterTypes();
System.out.println(Arrays.toString(types));
}
}

输出:

1
2
demo.Eoo[]
demo.Eoo[int, class java.lang.String, double, class java.lang.String]​

动态创建对象

调用无参构造器创建对象

如果没有无参数构造器,将发生异常!Class 提供了方法 newInstance()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testNewInstance() throws Exception{
/*
* 动态调用无参数构造器创建对象
* 1 动态加载类
* 2 利用class 的方法 newInstance 执行
* 无参数构造器常见对象
* 注意:类必须有无参数,否则出异常
*/
String className = "java.util.Date";
Class cls = Class.forName(className);
// cls.newInstance()调用无参数构造器创建对象
Object obj = cls.newInstance();
System.out.println(obj);

//静态的创建对象!编译已经就固定了!
Date date = new Date();
}

输出:

1
Fri Sep 16 20:04:55 CST 2016

调用有参构造器创建对象

如果没有对应有参数构造器!将发生异常!参数传递错误、将发生异常!

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
/**
* 调用 className 类名对应的类的有参数构造器,paramTypes 代表对应构造器的参数列表
* className + paramTypes 共同决定调用哪个构造器!执行构造器还需要具体的参数params
*/
public Object create(String className,Class[] paramTypes,Object[] params)
throws Exception{
// 动态加载类
// 动态获取指定参数类型的构造器
// 执行这个构造器,传递 params 参数。
Class cls = Class.forName(className);
//getDeclaredConstructor 在类信息中查找
//给定参数类型的构造器信息
Constructor c = cls.getDeclaredConstructor(paramTypes);
//执行构造器 c.newInstance() 方法,创建对象
//返回值就是这个构造器创建的对象
Object obj = c.newInstance(params);
return obj;
}
@Test
public void testCreate() throws Exception {
String className = "java.util.Date";
//类型列表==Class类型的数组
Class[] paramTypes = {long.class};
//实际参数列表
Object[] params={-1000L*60*60*24*365};
Object obj = create(className, paramTypes, params);
System.out.println(obj);
//思考:如何动态调用 new String("Hello");
className = "java.lang.String";
/*
* {} 只能拥有声明变量时候直接初始化
* 不能用于赋值语句!
* 赋值语句可以使用 new Object[]{"Hello"}
*/
paramTypes = new Class[]{String.class};
params = new Object[]{"Hello"};
obj = create(className, paramTypes, params);
System.out.println(obj);//Hello

//思考:如何动态调用 new String(byte[],"utf-8");
obj = create("java.lang.String",
new Class[]{byte[].class, String.class},
new Object[]{new byte[]{65,66,67,68}, "UTF-8"});
System.out.println(obj);
}

输出:

1
2
3
Wed Jan 01 08:00:00 CST 1969
Hello
ABCD​

动态获取类的属性值

实现过程(如何利用反射API实现动态属性访问):

1.找到对象的类型信息(方法区)

2.在信息中找属性信息(Field)

3.在对象上获取属性的值!

类Goo:

1
2
3
4
5
6
7
8
9
10
11
12
public class Goo {
public int id;
public String name;
public Goo() {

}
public Goo(int id, String name) {
super();
this.id = id;
this.name = name;
}
}

动态获取属性的值:

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
 /**
* 获取obj对象的 fieldName 对应属性的值
* @param obj
* @param fieldName
* @return 属性值
*/
public Object get(Object obj, String fieldName)throws Exception{
//1 获取类信息
/*
* Java 中对象的getClass()方法可以获取 对象的类型信息!
* Java 中 有3种方法可以获取Class信息
* 1. 类名.class 获取类信息(静态)
* 2. Class.forName("类名") 获取类信息 动态
* 3. obj.getClass() 获取类信息。运行期间,通过当前对象获取类信息
*/
Class cls = obj.getClass();
//找到属性:
/*
* getDeclaredField 按照属性名在cls中查找
* 类信息。 当属性没有找到时候,抛出异常!
*/
Field field = cls.getDeclaredField(fieldName);
//在对象上获取属性的值!
/*
* get方法:在一个对象上获取属性的值,对象上没有对应的属性,抛出异常
*/
Object value = field.get(obj);
return value;
}

@Test
public void testGetField()throws Exception {
/*
* 动态获取对象的属性
*/
Goo goo = new Goo(5, "Tom");
Object v1 = get(goo, "id");
Object v2 = get(goo, "name");
System.out.println(v1+" "+v2);
}

输出:

1
5 Tom

动态调用类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testinvoke()throws Exception{
List<String> list=new ArrayList<String>();
list.add("tom");
list.add("jack");
// 动态获取类信息
Class cls = list.getClass();
// 通过方法名和参数类型找到对应的方法
Method method = cls.getDeclaredMethod("remove", new Class[]{int.class});
// 调用方法,传递对象和具体参数
Object value=method.invoke(list,new Object[]{0});
System.out.println(value); //tom
}

反射的意义

常见的框架的底层都是使用反射实现的!如:Spring MyBatis Struts2 Hibernate …

现有application.xml文件:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="date" class="java.util.Date"></bean>
<bean id="list" class="java.util.ArrayList"></bean>
</beans>

模拟Spring框架的getBean()方法:

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
public class ApplicationContext {
private HashMap<String, Object> map= new HashMap<String, Object>();
public ApplicationContext(String xml) {
SAXReader reader = new SAXReader();
try{
//读取xml
InputStream in = this.getClass().getClassLoader().getResourceAsStream(xml);
Document doc = reader.read(in);
//解析XML内容 获取全部的<bean>
List<Element> beans = doc.getRootElement().elements();
for (Element e : beans) {
//e 是每个 <bean> 元素
String id = e.attributeValue("id");
String className = e.attributeValue("class");
//利用反射创建对象
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//对象缓存到 map中
map.put(id, obj);
}

}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
public Object getBean(String id){
return map.get(id);
}
public static void main(String[] args) {
String conf = "application.xml";
ApplicationContext ac = new ApplicationContext(conf);
Object o = ac.getBean("date");
System.out.println(o); //Fri Sep 16 21:30:07 CST 2016
}
}

请作者喝瓶肥宅水🥤

0