1 反射概述

基本概述

  1. 反射是指对于任何一个Class类,在”运行的时候”都可以直接得到这个类全部成分。

  2. 在运行时,可以直接得到这个类的构造器对象:Constructor

  3. 在运行时,可以直接得到这个类的成员变量对象:Field

  4. 在运行时,可以直接得到这个类的成员方法对象:Method

  5. 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。

反射的关键

反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分

反射的作用

  1. 可以在运行时得到一个类的全部成分然后操作。
  2. 可以破坏封装性。(很突出)
  3. 也可以破坏泛型的约束性。(很突出)
  4. 更重要的用途是适合:做Java高级框架
  5. 基本上主流框架都会基于反射设计一些通用技术功能。

2. 反射获取类对象

  • 方式一:Class c1 = Class.forName(“全类名”)

  • 方式二:Class c2 =类名.class

  • 方式三:Class c3 = 对象.getClass();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
目标:反射的第一步:获取Class对象
*/
public class Test {
public static void main(String[] args) throws Exception {
// 1、Class类中的一个静态方法:forName(全限名:包名 + 类名)
Class c = Class.forName("cn.jyw.demo.Student");

// 2、类名.class
Class c1 = Student.class;

// 3、对象.getClass() 获取对象对应类的Class对象。
Student s = new Student();
Class c2 = s.getClass();
}
}

3. 反射获取构造器对象

3.1 使用反射技术获取构造器对象并使用

  1. 获得class对象
  2. 获得Constructor对象
  3. 创建对象

3.2 Class类中用于获取构造器的方法

方法 说明
Constructor<?>[] getConstructors() 返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getDeclaredConstructors() 返回所有构造器对象的数组,存在就能拿到
Constructor<T> getConstructor(Class<?>… parameterTypes) 返回单个构造器对象(只能拿public的)
Constructor\<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造器对象,存在就能拿到
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
// 1. getConstructors:
// 获取全部的构造器:只能获取public修饰的构造器。
// Constructor[] getConstructors()
@Test
public void getConstructors(){
// a.第一步:获取类对象
Class c = Student.class;
// b.提取类中的全部的构造器对象(这里只能拿public修饰)
Constructor[] constructors = c.getConstructors();
// c.遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}


// 2.getDeclaredConstructors():
// 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
@Test
public void getDeclaredConstructors(){
// a.第一步:获取类对象
Class c = Student.class;
// b.提取类中的全部的构造器对象
Constructor[] constructors = c.getDeclaredConstructors();
// c.遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}

// 3.getConstructor(Class... parameterTypes)
// 获取某个构造器:只能拿public修饰的某个构造器
@Test
public void getConstructor() throws Exception {
// a.第一步:获取类对象
Class c = Student.class;
// b.定位单个构造器对象 (按照参数定位无参数构造器 只能拿public修饰的某个构造器)
Constructor cons = c.getConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());
}


// 4.getConstructor(Class... parameterTypes)
// 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
@Test
public void getDeclaredConstructor() throws Exception {
// a.第一步:获取类对象
Class c = Student.class;
// b.定位单个构造器对象 (按照参数定位无参数构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());

// c.定位某个有参构造器
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());

}

3.3 Constructor类中用于创建对象的方法

  1. 可以通过定位类的构造器对象。
  2. 如果构造器对象没有访问权限可以通过:void setAccessible(true)打开权限
  3. 构造器可以通过T newInstance(Object… initargs)调用自己,传入参数!
符号 说明
T newInstance(Object… initargs) 根据指定的构造器创建对象
public void setAccessible(boolean flag) 设置为true,表示取消访问检查,进行暴力反射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.调用构造器得到一个类的对象返回。
@Test
public void getDeclaredConstructor() throws Exception {
// a.第一步:获取类对象
Class c = Student.class;
// b.定位单个构造器对象 (按照参数定位无参数构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "===>" + cons.getParameterCount());

// 如果遇到了私有的构造器,可以暴力反射
cons.setAccessible(true); // 权限被打开
Student s = (Student) cons.newInstance();
System.out.println(s);

// c.定位某个有参构造器
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());
Student s1 = (Student) cons1.newInstance("孙悟空", 1000);
System.out.println(s1);
}

3.4 总结

  1. 利用反射技术获取构造器对象的方式
  • getDeclaredConstructors()
  • getDeclaredConstructor (Class<?>…parameterTypes)
  1. 反射得到的构造器可以做什么?
  • 依然是创建对象的

    • public newInstance(Object… initargs)
  • 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象

    • setAccessible(boolean)
    • 反射可以破坏封装性,私有的也可以执行了。

4. 反射获取成员变量对象

4.1 使用反射技术获取成员变量对象并使用

  1. 获得class对象
  2. 获得Field对象
  3. 赋值或者获取值

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

方法 说明
Field[] getFields() 返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields() 返回所有成员变量对象的数组,存在就能拿到
Field getField(String name) 返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String 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
/**
* 1.获取全部的成员变量。
* Field[] getDeclaredFields();
* 获得所有的成员变量对应的Field对象,只要申明了就可以得到
*/
@Test
public void getDeclaredFields(){
// a.定位Class对象
Class c = Student.class;
// b.定位全部成员变量
Field[] fields = c.getDeclaredFields();
// c.遍历一下
for (Field field : fields) {
System.out.println(field.getName() + "==>" + field.getType());
}
}

/**
2.获取某个成员变量对象 Field getDeclaredField(String name);
*/
@Test
public void getDeclaredField() throws Exception {
// a.定位Class对象
Class c = Student.class;
// b.根据名称定位某个成员变量
Field f = c.getDeclaredField("age");
System.out.println(f.getName() +"===>" + f.getType());
}

4.3 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
@Test
public void setField() throws Exception {
// a.反射第一步,获取类对象
Class c = Student.class;
// b.提取某个成员变量
Field ageF = c.getDeclaredField("age");

ageF.setAccessible(true); // 暴力打开权限

// c.赋值
Student s = new Student();
ageF.set(s , 18); // s.setAge(18);
System.out.println(s);

// d、取值
int age = (int) ageF.get(s);
System.out.println(age);

}

4.4 总结

  1. 利用反射技术获取成员变量的方式
  • getDeclaredFields()
  • getDeclaredField(String name)

2.反射得到成员变量可以做什么?

  • 依然是在某个对象中取值和赋值。

    • void set(Object obj, Object value)
    • Object get(Object obj)
  • 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值

    • setAccessible(boolean)

5. 反射获取方法对象

5.1 使用反射技术获取方法对象并使用

  1. 获得class对象
  2. 获得Method对象
  3. 运行方法

5.2 Class类中用于获取成员方法的方法

方法 说明
Method[] getMethods() 返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods() 返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes) 返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回单个成员方法对象,存在就能拿到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 1.获得类中的所有成员方法对象
*/
@Test
public void getDeclaredMethods(){
// a.获取类对象
Class c = Dog.class;
// b.提取全部方法;包括私有的
Method[] methods = c.getDeclaredMethods();
// c.遍历全部方法
for (Method method : methods) {
System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount());
}
}

5.3 Method类中用于触发执行的方法

Object invoke(Object obj, 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
/**
* 2. 获取某个方法对象
*/
@Test
public void getDeclardMethod() throws Exception {
// a.获取类对象
Class c = Dog.class;
// b.提取单个方法对象
Method m = c.getDeclaredMethod("eat");
Method m2 = c.getDeclaredMethod("eat", String.class);

// 暴力打开权限了
m.setAccessible(true);
m2.setAccessible(true);

// c.触发方法的执行
Dog d = new Dog();
// 注意:方法如果是没有结果回来的,那么返回的是null.
Object result = m.invoke(d);
System.out.println(result);

Object result2 = m2.invoke(d, "骨头");
System.out.println(result2);
}

5.4 总结

  1. 利用反射技术获取成员方法对象的方式

获取类中成员方法对象

getDeclaredMethods()

getDeclaredMethod (String name, Class<?>… parameterTypes)

  1. 反射得到成员方法可以做什么?

依然是在某个对象中触发该方法执行。

Object invoke(Object obj, Object… args)

如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行

setAccessible(boolean)

6. 反射的作用-绕过编译阶段为集合添加数据

反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素

泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了

  • 编译成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
    // 需求:反射实现泛型擦除后,加入其他类型的元素
ArrayList<String> lists1 = new ArrayList<>();
ArrayList<Integer> lists2 = new ArrayList<>();

System.out.println(lists1.getClass());
System.out.println(lists2.getClass());

System.out.println(lists1.getClass() == lists2.getClass()); // ArrayList.class

System.out.println("---------------------------");
ArrayList<Integer> lists3 = new ArrayList<>();
lists3.add(23);
lists3.add(22);
// lists3.add("黑马");

Class c = lists3.getClass(); // ArrayList.class ===> public boolean add(E e)
// 定位c类中的add方法
Method add = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean) add.invoke(lists3, "黑马");
System.out.println(rs);

System.out.println(lists3);

ArrayList list4 = lists3;
list4.add("白马");
list4.add(false);
System.out.println(lists3);
}

7. 反射的作用-通用框架的底层原理

需求

给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去

分析

  1. 定义一个方法,可以接收任意类的对象。
  2. 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
  3. 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
  4. 使用反射获取对象的Class类对象,然后获取全部成员变量信息。
  5. 遍历成员变量信息,然后提取本成员变量在对象中的具体值
  6. 存入成员变量名称和值到文件中去即可