1.类加载概要
类加载概要.png
2.class加载验证流程
- 加载
- 加载类的第一个阶段
- 取得类的二进制流
- 转为方法区数据结构
- 在Java堆中生成对应的java.lang.Class对象
- 链接
- 验证
- 目的:保证Class流的格式是正确的
- 文件格式的验证
- 是否以0xCAFEBABE开头
- 版本号是否合理
- 元数据验证
- 是否有父类
- 继承了final类?
- 非抽象类实现了所有的抽象方法
- 字节码验证(很复杂)
- 运行检查
- 栈数据类型和操作码数据参数吻合
- 跳转指令指定到合理的位置
- 符号引用验证
- 常量池中描述类是否存在
- 访问的方法或字段是否存在且有足够的权限
- 文件格式的验证
- 目的:保证Class流的格式是正确的
- 准备
- 分配内存,并为类设置初始值(方法区中)
- public static int v = 1;
- 在准备阶段中,v会被设置为0;
- 在初始化的<clinit>中才会被设置为1;
- 对于static final 类型,在准备阶段就会被附上正确的值;
- public static final int v = 1;
- 分配内存,并为类设置初始值(方法区中)
- 解析
- 验证
类加载-解析.png
- 初始化
-
执行类构造器<clinit>
- static变量 赋值语句
- static{} 语句
子类的<clinit>调用前保证父类的<clinit>被调用
<clinit>是线程安全的
-
初始化顺序
类加载初始化顺序1.pngpackage classloader; public class InitTest { // 静态变量 public static String staticField = "静态变量"; // 变量 public String field = "变量"; // 静态初始化块 static { System.out.println(staticField); System.out.println("静态初始化块"); } // 初始化块 { System.out.println(field); System.out.println("初始化块"); } // 构造器 public InitTest() { System.out.println("构造器"); } public static void main(String[] args) { new InitTest(); } }
输出结果:
静态变量
静态初始化块
变量
初始化块
构造器类加载初始化顺序2.pngpackage classloader; class Parent { // 静态变量 public static String p_StaticField = "父类--静态变量"; protected int i = 1; protected int j = 8; // 变量 public String p_Field = "父类--变量"; // 静态初始化块 static { System.out.println(p_StaticField); System.out.println("父类--静态初始化块"); } // 初始化块 { System.out.println(p_Field); System.out.println("父类--初始化块"); } // 构造器 public Parent() { System.out.println("父类--构造器"); System.out.println("i=" + i + ", j=" + j); j = 9; } } public class SubClass extends Parent { // 静态变量 public static String s_StaticField = "子类--静态变量"; // 变量 public String s_Field = "子类--变量"; // 静态初始化块 static { System.out.println(s_StaticField); System.out.println("子类--静态初始化块"); } // 初始化块 { System.out.println(s_Field); System.out.println("子类--初始化块"); } // 构造器 public SubClass() { System.out.println("子类--构造器"); System.out.println("i=" + i + ",j=" + j); } // 程序入口 public static void main(String[] args) { new SubClass(); } }
输出结果:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
i=1, j=8
子类--变量
子类--初始化块
子类--构造器
i=1,j=9
-
3.什么是类加载器ClassLoader
- ClassLoader是一个抽象类
- ClassLoader的实例将读入Java字节码将类加载到JVM中
- ClassLoader可以定制,满足不同的字节码流获取方式
- ClassLoader负责类加载过程中的加载阶段
3.JDK中ClassLoader默认设计模式
- ClassLoader的重要方法
- public Class<?> loadClass(String name) throws ClassNotFoundException
- 载入并返回一个Class
- protected final Class<?> defineClass(byte[] b, int off, int len)
- 定义一个类,不公开调用
- protected Class<?> findClass(String name) throws ClassNotFoundException
- loadClass回调该方法,自定义ClassLoader的推荐做法
- protected final Class<?> findLoadedClass(String name)
- 寻找已经加载的类
- public Class<?> loadClass(String name) throws ClassNotFoundException
- 分类
- BootStrap ClassLoader(启动ClassLoader)
- Extension ClassLoader(扩展ClassLoader)
- App ClassLoader(应用ClassLoader/系统ClassLoader)
- Custom ClassLoader(自定义ClassLoader)
- 每个ClassLoader都有一个Parent作为父亲
- 协同工作
协同工作.png
loadClass.png
类加载例子.png
- 直接运行以上代码:
- I am in apploader
- 加上参数 -Xbootclasspath/a: D:<path>
- I am in bootloader
- 此时AppLoader中不会加载HelloLoader
- I am in apploader在classpath中却没有加载
- 说明类加载是从上往下的
强制AppLoader.png
- 问题
类加载问题.png
- 解决
- Thread.setContextClassLoader()
- 上下文加载器
- 是一个角色
- 用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题
- 基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例
- Thread.setContextClassLoader()
类加载例.png
- 双亲模式的破坏
- 双亲模式是默认的模式,但不是必须这么做
- Tomcat的WebappClassLoader就会先加载自己的Class,找不到再委托parent
- OSGI的ClassLoader形成网状结构,根据需要自由加载Class
破坏双亲模式例子1.png
findClass.png
破坏双亲模式例子2.png
AppLoader结果.png
5.热替换
热替换1.png
热替换2.png