反射

框架设计的灵魂,反射机制就是指将类的各个组成部分封装成其他对象

框架

半成品的软件,可以在其基础上进行软件开发,简化代码

反射机制的好处

  • 可以在程序运行过程中操作这些对象
  • 可以解耦,提高程序可扩展性

Java代码在计算机中的三个运行阶段

java代码三个运行阶段

(第二阶段通过类加载器将源代码阶段的字节码文件加载进了内存)

三个阶段获取Class对象的方法

源代码阶段

Class.forName(“全类名”):将字节码文件加载进内存,返回Class类对象

多用于配置文件,将类名定义在配置文件中,读取文件,加载类

Class类对象阶段

通过 类名.class:通过类名的属性class获取

多用于参数传递时

运行时阶段

通过 对象.getClass() 获取(getClass方法定义在Object类中,所以所有对象都具有该方法)

多用于对象已被创建后,通过对象获取字节码文件

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
/**
* 获取Class对象的三种方法
*/
public class ReflectDemo1 {

public static void main(String[] args) throws ClassNotFoundException {

//Class.forName("全类名")
Class cls1=Class.forName("cn.ywrby.domain.Person");
System.out.println(cls1);
//类名.class
Class cls2= Person.class;
System.out.println(cls2);
//对象.getClass()
Person person=new Person();
Class cls3=person.getClass();
System.out.println(cls3);

//判读Class对象是否为同一个
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
//结果均为true说明一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次
//也就是说不论通过哪一种方式获取到的Class对象都是同一个
}

}

使用Class对象

获取功能

  • 获取成员变量们
    • Field[] getFields() 获取所有public修饰的成员变量
    • Field getField(String name) 获取指定名称的由public修饰的成员变量 (通过Filed对象的get()和set()方法可以获取和设置成员变量的值,不过需要传入对象参数)
    • Field[] getDeclaredFields() 获取所有的成员变量,不考虑变量的修饰符
    • Field getDeclaredField(String name) 获取指定成员变量,不考虑修饰符
  • 获取构造方法们
    • Constructor<?>[] getConstructors()
    • Constructor getConstructor(Class<?>… parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
    • Constructor getDeclaredConstructor(Class<?>… parameterTypes)
  • 获取成员方法们
    • Method[] getMethods()
    • Method getMethod(String name, Class<?>… parameterTypes)
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, Class<?>… parameterTypes)
  • 获取类名
    • String getName()
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
//获取成员变量们
public class ReflectDemo2 {

public static void main(String[] args) throws Exception {

//首先获取Person的Class对象
Class personClass=Person.class;
//获取所有public修饰的成员变量
Field[] fields=personClass.getFields();
for (Field field: fields) {
System.out.println(field);
}

System.out.println("--------------------");

//获取指定的public修饰的成员变量
Field field=personClass.getField("num");
//设置成员变量的值
Person p=new Person();
field.set(p,80);
//获取成员变量的值
Object num= field.get(p);
System.out.println(num);

System.out.println("--------------------");

//获取所有的成员变量
Field[] fields1=personClass.getDeclaredFields();
for (Field field1:fields1) {
System.out.println(field1);
}

System.out.println("--------------------");

//获取指定的成员变量
Field field1=personClass.getDeclaredField("name");
/* 设置成员变量的值(正常情况下我们直接通过对象无法获取到对象所在类
* 的非public类型的成员变量,但是通过反射的getDeclaredField等方法却可以获取到
* 任意指定的成员变量,但如果该成员变量是非public修饰的,我们需要忽略访问权限修饰符的安全检查
* 才能对该成员变量进行访问(获取或设置值))
*/
field1.setAccessible(true); //忽略访问权限修饰符的安全检查(被称为暴力反射)
field1.set(p,"ywrby");
Object name=field1.get(p);
System.out.println(name);

}
}

/* 运行结果
public int cn.ywrby.domain.Person.num
--------------------
80
--------------------
private java.lang.String cn.ywrby.domain.Person.name
private int cn.ywrby.domain.Person.age
public int cn.ywrby.domain.Person.num
--------------------
ywrby
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//获取构造方法们
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {

//首先获取Person的Class对象
Class personClass = Person.class;
//获取构造函数/构造器(构造函数间唯一区别就在于参数列表,所以通过参数类型获取指定构造器)
Constructor constructor=personClass.getConstructor(String.class,int.class,int.class);
//利用构造方法可以用来创建对象 newInstance​(Object... initargs)方法就是用来创建对应的对象
Object person=constructor.newInstance("ywrby",19,95);
System.out.println(person); //这里记得在Person类中重写toString方法才能显示正确的各变量值(右键generate快速创建)
//空参构造器一般直接采用下面这种方式简化创建
Object person2=personClass.getDeclaredConstructor().newInstance();
System.out.println(person2);

}
}

/* 运行结果
Person{name='ywrby', age=19, num=95}
Person{name='null', age=0, num=0}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//获取成员方法
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {

//首先获取Person的Class对象
Class personClass = Person.class;
//获取指定方法,第一个参数是方法名,后续参数的方法的参数列表类型
Method method = personClass.getDeclaredMethod("MessageOfPerson", int.class);
//执行方法
Person person = new Person("ywrby", 19, 95);
method.invoke(person, 5); //利用invoke执行方法,第一个参数是类对象,后续不定参是方法实参列表
//方法对象还可以直接获取名称,利用getName()方法
}
}

/*
name is ywrby, age is 19, num is 95
name is ywrby, age is 19, num is 95
name is ywrby, age is 19, num is 95
name is ywrby, age is 19, num is 95
name is ywrby, age is 19, num is 95
*/

反射的简单案例

完成一个简单的框架,在不改变任何代码的前提下,能够创建任意类对象,并且运行其中任意方法(方法无参)

实现

  • 配置文件
  • 反射

步骤

  1. 将需要创建的全类名和需要执行的方法名定义在配置文件中
  2. 在程序中加载读取配置文件
  3. 使用反射技术来加载读取类文件进入内存
  4. 创建对象
  5. 执行方法

配置文件

1
2
className=cn.ywrby.domain.Person
methodName=MessageOfPerson

简单框架

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
public class ReflectTest {

public static void main(String[] args) throws Exception {
//加载配置文件
//创建Properties对象
Properties pro = new Properties();
//利用load方法加载配置文件,转换为一个集合
ClassLoader classLoader=ReflectTest.class.getClassLoader(); //获取类加载器
InputStream is=classLoader.getResourceAsStream("pro.properties"); //利用类加载器的getResourceAsStream方法获取资源路径下的字节流对象
pro.load(is);

//获取配置文件中定义的数据
String className=pro.getProperty("className");
String methodName=pro.getProperty("methodName");

//加载该类进内存,并且创建对象
Class cls=Class.forName(className);
//创建对象
Object object=cls.getDeclaredConstructor().newInstance();
//获取方法对象
Method method=cls.getMethod(methodName);
//执行方法
method.invoke(object);
}
}