类加载器
执行流程
-
编译
- 在 Java 中 .java 文件是 Java 源文件,需要通过 Java 编译器(javac)将其编译成字节码文件,即.class文件。这个编译过程会将 Java 源文件中的代码转换为 JVM 能够理解的字节码指令。
-
程序启动
==注:== 当程序启动后, 并不会直接进行类加载, Java 采用了延迟加载机制, 只有当程序实际使用到某个类时, 才会触发类加载过程。
- 类的延迟加载机制
- 当程序首次主动使用某个类时,才会将该类的字节码载入方法区。主动使用的情况包括创建类的实例、访问类的静态变量或静态方法、调用类的构造方法等。
- 类的延迟加载机制
-
类加载
-
加载阶段
- 类加载器负责查找字节码文件,并将其以二进制流的方式读入内存 (读入方法区) ,在内存 (方法区) 中生成一个代表该类的java.lang.Class对象。这个对象是后续在 Java 程序中访问和使用该类的基础
-
链接阶段
-
验证阶段 : 确保被加载的类的字节码符合 JVM 规范,没有安全隐患,例如检查字节码的格式是否正确、是否存在非法的指令等。
-
准备阶段 : 为类的静态变量分配内存空间,并设置默认初始值。例如,对于int类型的静态变量,会将其初始值设为 0;对于Object类型的静态变量,会将其初始值设为null。
-
解析阶段 : 将类中的符号引用(全类名定位符)转换为直接引用。当字节码文件还未被加载时, 类中的各个类名,方法名等等都只是一个全类名定位符, 用于描述逻辑上的关系. 解析就是给这个定位符指向一块内存中的地址, 相当于定金, 但不会分配具体运行时内存容量.
==注:== 解析阶段不直接为字段、方法或类实例分配运行时内存,而是基于类文件的元数据和符号引用来确定布局和引用关系。JVM 通过字节码描述符、常量池以及元数据结构,能够在解析阶段准确判断字段和方法的容量需求,而实际的内存分配则延迟到类初始化或对象实例化时进行。
-
-
初始化阶段
- 执行类的初始化代码,包括静态变量的显式赋值和静态代码块中的语句。这个阶段是类加载的最后一步,完成后类就可以被正常使用了。
-
类加载器的分类
- Bootstrap ClassLoader:启动类加载器
- 最顶层的类加载器,主要负责加载 Java 的核心类库,如java.lang、java.util等包下的类。这个类加载器是由 C++ 实现的,是 Java 虚拟机的一部分,它没有父类加载器。
- 输出打印为null
- Extension ClassLoader: 扩展类加载器
- 上级加载器是 Bootstrap ClassLoader,主要负责加载 Java 的扩展类库,即%JRE_HOME%\lib\ext目录下的所有类库,或者由java.ext.dirs系统属性指定的目录下的类库。(jdk9以前)
- 输出打印为ExtClassLoader
- Application ClassLoader:应用程序类加载器
- 上级加载器是 Extension ClassLoader,也称为系统类加载器,是负责加载应用程序 classpath 下的类库。一般来说,我们编写的 Java 代码所生成的类,都是由这个类加载器来加载的
- 输出打印为AppClassLoader
- ==注:== 上述三个加载器无继承关系, 三个加载器都是继承BuiltinClassLoader类的, 属于同级别. 只是进行类加载时, 层级不同而已, 应用程序类加载器为最底层, 启动类加载器为最顶层. 当需要进行类加载时, 会先判断最底层构造器是否加载, 依次往上. (双亲委派机制)
- 自定义类加载器:
类加载器的工作机制
- 双亲委派机制 : 和Hash表原理差不多
- 如果一个类加载器收到了类加载请求,它并不会自己先去加载
- 而是把这个请求委托给父类的加载器去执行
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
- 请求最终将到达顶层的启动类加载器
- 如果父类加载器可以完成类加载任务,就成功返回
- 倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
- 作用 : 避免类的重复加载
- 示例图
反射机制
作用
- 当程序首次主动使用某个类时, 会进行类加载, 根据对应 .class 文件生成Class对象, 一个类对应一个Class对象, 操作Class对象, 就是操作对应类的class文件, 反射就是通过获取 Class 对象进行操作 .class 文件 (对格式化的数据进行操作)
Class实现类测试
-
获取Class对象
-
//使用全类名获取Class对象 Class<?> aClass = Class.forName("com.itheima.pojo.Student"); //使用类名.class获取Class对象 Class<Student> bClass = Student.class; //使用Object类继承的getClass()方法获取Class对象 Class<? extends Student> cClass = new Student().getClass();
-
-
Class对象作用
- 获取类的成员
- 获取构造器
- Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
- Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组 (暴力反射)
- Constructor getConstructor(Class<?>... para) 返回单个公共构造方法对象
- Constructor getDeclaredConstructor(Class<?>... para) 返回单个构造方法对象(暴力反射)
- 获取成员变量
- 获取成员方法
- 获取构造器
- 创建类的实例
- T newInstance(Object...para) 根据指定的构造方法创建对象
- 判断类的关系
- 获取类的父类和接口
- 获取类的成员
注解
概述与作用
-
常用方式
-
框架标注
-
案例一
-
原理 : 通过反射根据传入的Class对象, 获取到对象中的所有方法数组, 然后依次判断方法是否被数组标注, 如此注解的第一个作用就是做一个标记, 然后使用 isAnnotationPresent() 方法判断是否被标注, 然后进行处理
-
public class MyAnnoTest { public static void main(String[] args) throws Exception { methodTest(AnnoDemo.class); } //需求: 设计框架, 要求只要标注@MyTest注解的方法, 就会执行 public static void methodTest(Class clazz) throws Exception { //通过传入的Class对象,获取所有的方法 Method[] methods = clazz.getMethods(); //遍历所有方法,判断上方是否有@MyTest注解 for (Method method : methods) { //MyTest.class : @MyTest注解的 if (method.isAnnotationPresent(MyTest.class)){ Class对象 //执行方法 method.invoke(clazz.newInstance()); } } } }
-
-
代替配置文件
- XML配置文件开发原理
- 使用servlet开发时, tomcat 会读取 web.xml 中配置的信息, 存储在map集合中. 然后根据请求路径 (键) 找到值 (servlet对象全类名) , 然后根据反射机制创建 servert 对象.
- 注解的具体工作原理
- 程序启动时, Tomcat 使用类加载器扫描项目的所有
.class文件, 然后通过反射检查是否存在如@WebServlet、@WebFilter等注解 使用 isAnnotationPresent(MyTest.class) 方法. 一旦发现了符合条件的类,Tomcat 会将注解中的信息(如 URL 映射)存储到内部的映射表中,类似于web.xml中的配置信息。后面也就一样了.
- 程序启动时, Tomcat 使用类加载器扫描项目的所有
- XML配置文件开发原理
-
自定义注解
-
格式
-
public @interface MyTest { /** * 换个写法,与此对应 * name() ==> name * default ==> = *String name="liny"; */ String name() default "liny"; int age() default 23; //没有赋值,使用该注解时,必须传入值 String address(); Class clazz() default String.class; int[] arr() default {1, 2, 3}; }
元注解
-
作用, 使用在注解中, 为定义的注解添加规则
-
@Retention (RetentionPolicy.RUNTIME) : 可以理解为保留时间(生命周期)
-
public enum RetentionPolicy { //编译 SOURCE, CLASS, RUNTIME }
-
-
@Target (ElementType.ANNOTATION_TYPE) : 指定了注解能在哪里使用
-
public enum ElementType { TYPE //类,接口 FIELD //成员变量 METHOD //成员方法 PARAMETER //方法参数 CONSTRUCTOR //构造方法 LOCAL_VARIABLE //局部变量 }
-