佛山网站定制,门户网站开源,网站平台做推广,福州网站开发系列一、类加载的意义与整体流程 在Java中#xff0c;每一个.java文件经过编译都会生成.class字节码文件。但字节码本身并不能直接运行#xff0c;必须通过 类加载#xff08;Class Loading#xff09;将其转化为JVM内存中的数据结构#xff0c;才能被程序调用。 类加载过程就…一、类加载的意义与整体流程 在Java中每一个.java文件经过编译都会生成.class字节码文件。但字节码本身并不能直接运行必须通过 类加载Class Loading将其转化为JVM内存中的数据结构才能被程序调用。 类加载过程就像一座精密的工厂流水线将原材料字节码加工成可运行的类对象。 类的完整生命周期包含7个阶段加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载。其中验证、准备、解析统称为 连接Linking阶段。 二、类加载的五大阶段详解
1. 加载Loading字节码的搬运工
核心任务将字节码载入方法区并生成Class对象
二进制获取通过全限定名如java.lang.String获取字节流来源包括JAR包、网络、动态代理等数据结构转换将静态存储结构转换为方法区的运行时结构Class对象生成在堆内存中创建java.lang.Class实例作为访问入口 类加载器小贴士加载工作由类加载器完成采用双亲委派机制确保安全。例如加载java.lang.Object时最终由启动类加载器完成。 2. 验证Verification安全守门员
四重安全检测机制 文件格式验证魔数检查是否以0xCAFEBABE开头、版本号验证等元数据验证语义检查是否继承final类、抽象方法实现等字节码验证程序逻辑验证类型转换是否合法、跳转指令正确性等符号引用验证确保后续解析能正常执行检查引用的类/方法是否存在 四重安全检测机制 // 示例符号引用验证失败案例
public class Demo {public void test() {// 如果SomeClass不存在解析时会抛出NoClassDefFoundErrorSomeClass.doSomething(); }
}
3. 准备Preparation内存分配的预演
类变量内存分配为static变量分配内存JDK7存储在堆中初始值设定 普通static变量设置数据类型的零值int0, booleanfalse等final static变量直接赋代码中的值
public class Example {public static int value 123; // 准备阶段value0public static final int F_VALUE 456; // 准备阶段F_VALUE456
}
4. 解析Resolution符号到引用的转换
将常量池中的符号引用替换为直接引用
符号引用以一组符号描述所引用的目标如全限定名直接引用指向目标的指针、偏移量等能直接定位的内存地址
符号引用类型转换目标类/接口方法区类型信息字段内存偏移量方法方法表索引
5. 初始化Initialization真正的诞生时刻
执行类构造器clinit()方法完成
static变量的赋值static代码块的执行
初始化触发条件满足任一即触发 new实例对象、访问/设置静态字段非final、调用静态方法反射调用如Class.forName()初始化子类时发现父类未初始化JVM启动时指定的主类使用MethodHandle/VarHandle的调用点 public class InitDemo {static {System.out.println(静态代码块执行); }public static int value initValue();private static int initValue() {System.out.println(静态方法调用);return 100;}
}
// 首次访问InitDemo.value时输出顺序
// 静态代码块执行 → 静态方法调用
三、类卸载生命的终结
卸载三要素 该类的所有实例已被GC回收没有其他地方引用该类加载该类的ClassLoader实例已被GC 重要特性由系统类加载器加载的类几乎不会被卸载而自定义类加载器加载的类可能被卸载。例如OSGi框架通过自定义加载器实现模块热部署。 四、高频面试问题解析
Q1准备阶段和初始化阶段对static变量的处理有何不同
准备阶段设置默认零值初始化阶段执行代码中的赋值操作
Q2以下代码会输出什么
public class Singleton {private static Singleton instance new Singleton();public static int value1;public static int value2 0;private Singleton() {value1 1;value2 1;}public static void main(String[] args) {System.out.println(Singleton.value1); // 输出1System.out.println(Singleton.value2); // 输出0}
}
解析初始化顺序导致value2被覆盖。具体执行顺序 准备阶段instancenull, value10, value20初始化阶段 执行new Singleton() → value11, value21执行value2显式赋值 → value20 Q3如何打破双亲委派模型 通过重写ClassLoader的loadClass()方法典型应用 TomcatWeb应用类隔离SPI机制线程上下文类加载器 码字不易希望可以一键三连我们下期文章再见