1. 类加载的过程
- 类的生命周期:加载 --> 连接(
验证 --> 准备 --> 解析
) --> 初始化 --> 使用 --> 卸载 - Java 里可以动态扩展的语言特性就是依赖运行期动态加载和动态连接实现的
- 加载、验证、准备、初始化和卸载这 5 个阶段顺序是确定的,解析阶段则不一定
- 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制
- 字节码文件(.clss) —> 类加载子系统 —> 运行时数据区
1.1 加载
- “加载” 是 “类加载” 过程的一个阶段
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将 class文件读入内存,并为之创建一个Class对象(任何类被使用时系统都会建立一个Class对象)
1.2 验证
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
1.3 准备
- 为类的静态成员分配分配内存,并设置默认值(静态随着类的加载而加载)
1.4 解析
- 将常量池内的符号引用转换为直接引用
- 符号引用,javac class 时生成助记符,当解析时会转化为直接引用,分配内存
- 直接引用,真正分配内存空间
- 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符 7 类符号
1.5 初始化
- 以下 5 种情况会初始化(加载、验证、准备需在此之前开始)
- 调用静态方法
- 反射调用
- 初始化子类会引起父类的主动使用
- wmain()
- new 对象时的 静态代码块
- 访问类的静态变量
2. ClassLoader
- 类加载器
- 构造器:构造类
- 深入理解类加载器
2.1 Java 9 之前
- BootStrap ClassLoader 启动类加载器(根加载器) JAVA_HOME/jre/lib
- ExtClassLoader 扩展类加载器 JAVA_HOME/jre/lib/ext
- AppClassLoader 应用类加载器(系统类加载器) ,程序中默认的类加载器,加载用户类路径(
ClassPath
)所指定的类
ClassLoader classLoader = Demo.class.getClassLoader();
// sun.misc.Launcher$AppClassLoader@18b4aac2; sun.misc.Launcher$ExtClassLoader@5479e3f
System.out.println(classLoader + "; " + classLoader.getParent());
System.out.println(int.class.getClassLoader()); // null
// 调用defineClass方法将class文件的字节码数组转为class类实例
clazz = defineClass(name, classData, 0, classData.length);
2.2 Java 9 之后
- BootStrap ClassLoader 加载 lib/modules
- Platform classloader(即 ExtClassLoader),加载lib/modules
- Application classloader 加载-cp,-mp指定的类
// null, 根加载器并不是由Java语言实现的,因此拿不到根加载器对象
ClassLoader classLoader = Object.class.getClassLoader();
// sun.misc.Launcher$ExtClassLoader
ClassLoader classLoader1 = DNSNameService.class.getClassLoader();
// sun.misc.Launcher$AppClassLoader
ClassLoader classLoader2 = Demo.class.getClassLoader();
ClassLoader classLoader = SortDemo.class.getClassLoader();
System.out.println(classLoader); // jdk.internal.loader.ClassLoaders$AppClassLoader@73d16e93
System.out.println(classLoader.getParent()); // jdk.internal.loader.ClassLoaders$PlatformClassLoader@58372a00
System.out.println(classLoader.getParent().getParent()); // 根加载器无法直接加载,null
// ClassLoader 的默认实现就是双亲委托
public abstract class ClassLoader {
//每个类加载器都有个父加载器
private final ClassLoader parent;
}
// 打印类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
- 首先根据源码可以知道,如果自定义的方法不想违背双亲委派模型,则只需要重写findclass()即可,如果想违背双亲委派模型,则还需要重写loadclass()
- ClassLoader 必须实现 findClass(String);URLClassLoader 已经实现 findClass(String)
- defineClass() 将 class 二进制内容转为Class对象
双亲委派模型
image.pngimage.png
Reference
结束语
有节奏的坚持
网友评论