java编译 & 反射

81 阅读3分钟

编译

Snipaste_2023-02-28_14-02-27.png

javac将.java文件编译为.class字节码文件,这个编译过程为前端编译,大致包含词法分析、语法分析、语义分析,之后.class字节码文件再由JVM处理,实现跨平台运行,JVM中有两种执行方式,先通过解释器将.class字节码文件一句句解释执行,这个过程偏慢,同时JIT编译器(just-in-time compilation)会统计经常重复执行的代码段,将其编译为机器码,并保存下来方便以后执行时直接调用,而不用再解释执行,这个编译过程为后端编译。

image.png

反射

正常创建对象:

Person p = new Person();
graph TD
id1[Person.class]
id2[Person的class对象]
id3[Person的对象p]
Person.java --javac--> id1 --ClassLoader--> id2 --new--> id3

动态代理场景

假设一个函数要执行某个对象的方法,因为这是一个通用函数,会接收不同的对象,这些对象也有各自的方法,方法名并不相同,在编写这个通用函数时是不知道会传入怎样的对象,这个对象的方法名又是什么,所以无法通过对象.方法的方式写死,这时就要依靠反射,在运行时动态地获取类的各种信息,包括类的属性、方法、构造方法。

动态代理正是要实现这样一个通用的代理函数,为被代理对象的方法添加额外功能,同时要执行原方法。只有在运行时才能确定被代理对象是谁,写代码时不能写死

反射用法

  • 首先要获取目标类的class对象,这是创建目标类对象的模板,包含类的全部信息
  1. 知道具体类
Class personClass = Person.class;
  1. 通过 Class.forName()传入类的全路径获取
Class personClass = Class.forName("cn.PersonClass");
  1. 通过对象实例instance.getClass()获取
Person p = new Person();
Class personClass = p.getClass();
  1. 通过类加载器xxxClassLoader.loadClass()传入类路径获取
ClassLoader.getSystemClassLoader().loadClass("cn.PersonClass");

动态代理就可以通过第3种方式实现

  • 通过class对象获取类的方法
// 获得全部方法,包含私有方法
Method[] methods = personClass.getDeclaredMethods();
// 获得全部方法,不含私有方法
Method[] methods = personClass.getMethods();
// 获得指定方法,包含私有方法
Method method = personClass.getDeclaredMethod("method",String.class);
// 获得指定方法,不含私有方法
Method method = personClass.getMethod("method",String.class);
// 调用指定方法
method.invoke(p, "parameter");
// 调用私有方法
method.setAccessible(true);
method.invoke(p, "parameter");
  • 通过class对象获取类的属性(成员变量)
Field[] fs = personClass.getFields();
for(Field f : fs){
    System.out.println(f.getName());  //属性名
    System.out.println(f.getType());  //属性类型
}
  • 通过class对象创建实例
Person p = (Person) personClass.newInstance();

Spring IOC容器场景

Spring IOC容器帮我们创建和管理各个实例对象,我们通过配置文件或注解的形式,告诉Spring需要创建哪些对象,以及这些对象之间有何依赖关系,然而Spring的开发者在编写代码时,并不知道用户需要如何创建对象,这便需要利用反射的手段

//拿到全类名,用于定位类,这一步一般Spring是通过扫描项目路径来获取,这一步是动态获取
String className = "com.demo.QueryService";
//反射获取类的Class对象
Class<?> clazz=Class.forName(className);
//如果该类标注有Service注解,我们就实例化这个类
if(clazz.isAnnotationPresent(Service.class){
   Object instance = clazz.newInstance();
   //用map来模拟容器
   map.put(clazz.getSimpleName(),instance);
}