反射
# 为什么用反射
# 反射作用
动态编译与静态编译:
- 动态编译:运行时确定类型,绑定对象;最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性;
- 静态编译:编译时确定类型,绑定对象;
反射可以在程序运行时分析类;
# 反射应用场景
- JDK动态代理,使用了反射类 Method 来调用指定方法;
- 注解,如
@Value
基于反射分析类,获取到类/属性/方法/方法的参数;
# 反射优缺点
优点:实现动态创建对象和编译,体现出很大的灵活性;
一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
缺点:对性能有影响;
反射是一种解释操作,告诉JVM希望做什么并让它满足我们的要求,这类操作总是慢于只直接执行相同的操作;
# Class类
所有类的类;
其构造器是私有的,只有JVM可以创建Class对象,不能new,但是可以通过已有的类得到一个Class对象;
Class c1 = Dog.class;//这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = cat.getClass();//cat是一个对象实例,通过一个类的对象的getClass()方法获得的;
Class c3 = Class.forName("com.Nreal.Animal");
1
2
3
2
3
可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法;
# 反射相关操作
Java 基础与提高干货系列——Java 反射机制 - 掘金 (juejin.cn) (opens new window)
# 通过反射了解集合泛型本质
Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了;
案例:给List<String>添加一个int类型元素;
public class GenericEssence {
public static void main(String[] args) {
List list1 = new ArrayList();
List<String> list2 = new ArrayList<String>();
list2.add("hello");
// list2.add(20); // 报错!list2有泛型限制,只能添加String,添加int报错
System.out.println("list2的长度是:" + list2.size()); // 此时list2长度为1
/*
* 然后通过反射添加元素方式,在运行期动态加载类,首先得到list1和list2
* 的类类型相同,然后再通过方法反射绕过编译器来调用add方法,看能否插入int
* 型的元素
*/
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2); // 结果:true,说明类类型完全相同
// 验证:我们可以通过方法的反射来给list2添加元素,这样可以绕过编译检查
try {
Method m = c2.getMethod("add", Object.class); // 通过方法反射得到add方法
m.invoke(list2, 20); // 给list2添加一个int型的,上面显示在编译器是会报错的
System.out.println("list2的长度是:" + list2.size()); // 结果:2,说明list2长度增加了,并没有泛型检查
} catch (Exception e) {
e.printStackTrace();
}
/*
* 综上可以看出,在编译期的时候,泛型会限制集合内元素类型保持一致
* 运行期以后,泛型就不再起作用了,即使是不同类型的元素也可以插入集合。
*/
}
}
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
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