一、反射:
https://www.daidingkang.cc/2017/07/18/java-reflection-annotations/(详解)
反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,
用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。
①反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理(ps:这个知识点也很重要,后续会为大家讲到)
②Java 反射机制的应用场景
- 逆向代码 ,例如反编译
- 与注解相结合的框架 例如Retrofit
- 单纯的反射机制应用框架 例如EventBus
- 动态生成类框架 例如Gson
③反射机制的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
-
静态编译:在编译时确定类型,绑定对象,即通过。
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
优点
- 可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
缺点
- 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
想要了解反射首先理解一下Class类,它是反射实现的基础,类是java.lang.Class类的实例对象,而Class是所有类的类,
获取类对象三种方式:在一个JVM中,一种类,只会有一个类对象存在,三个等同。
1. Class.forName
2. Hero.class
3. new Hero().getClass()
String className = "charactor.Hero";
Class pClass1=Class.forName(className);
Class pClass2=Hero.class;
Class pClass3=new Hero().getClass();
⑤获取成员变量,方法,构造器
在这里先看一下sun为我们提供了那些反射机制中的类:
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
前面
我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
总结如下:
- 获取成员方法Method
- 获取成员变量Field
- 获取构造函数Constructor
⑥通过反射了解集合泛型本质:
Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。
集合泛型的本质
*/
public class GenericEssence {
public static void main(String[] args) {
List list1 = new ArrayList(); // 没有泛型
List<String> list2 = new ArrayList<String>(); // 有泛型
/*
* 1.首先观察正常添加元素方式,在编译器检查泛型,
* 这个时候如果list2添加int类型会报错
*/
list2.add("hello");
// list2.add(20); // 报错!list2有泛型限制,只能添加String,添加int报错
System.out.println("list2的长度是:" + list2.size()); // 此时list2长度为1
/*
* 2.然后通过反射添加元素方式,在运行期动态加载类,首先得到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();
}
/*
* 综上可以看出,在编译器的时候,泛型会限制集合内元素类型保持一致,但是编译器结束进入
* 运行期以后,泛型就不再起作用了,即使是不同类型的元素也可以插入集合。
*/
}
}
思维导图:
- 概念
- 注解即元数据,就是源代码的元数据
- 注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
- Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
- 作用
- 生成文档
- 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
- 在编译时进行格式检查,如@Override等
- 每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程。