类加载是运行时环境的一个重要核心功能。
类加载的主要功能:把 .class 文件加载到内存中构建成为类对象。
什么时候会进行类加载呢 ?
不是说 java 程序一运行,就把所有的类都加载了,而是用到的时候再加载。(懒汉模式)
那什么时候算是用到了呢 ?
类的生命周期是这样的:
其中前 5 步是固定的顺序并且也是类加载的过程,其中 中间的 3 步都属于连接,所以对于类加载来
说总共分为以下几个步骤:
下面我们分别来看每个步骤的具体执行内容。
“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段,在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:
.class 文件就是把 .java 文件中核心信息都表达出来,只不过组织格式发生了变化。
Loading 会把从 .class 文件中解读到的信息初步填写到类对象中。
验证是连接阶段的第一步,这一阶段的目的是确保 Class文件的字节 流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信 息被当作代码运行后不会危害虚拟机自身的安全。
验证选项:
如果不符合规范类加载就会失败,抛出异常。
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。
注意这里面设置的初始值指的是默认值
比如此时有这样一行代码:
public static int value = 123;
它是初始化 value 的 int 值为 0,而非 123。
解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。
注意:常量池中不是只有 字符串常量
运行时常量池: 存放字面量与符号引用。
举个栗子:
class Example {
public int count = 0;
public void increment() {
count++;
}
}
public class Main {
public static void main(String[] args) {
Example e = new Example();
int currentCount = e.count; // 引用Example类的count字段
e.increment(); // 调用Example类的increment方法
}
}
解析 e.count:在这个步骤中,虚拟机将符号引用 e.count 解析为实际的内存地址,以便能够访问count字段的值。
解析 e.increment():虚拟机将符号引用 e.increment() 解析为实际的方法内存地址,以便能够正确地调用increment方法。
到这里, 你可能有些疑惑 ?这个实例属性 count 不是在运行阶段创建对象时才分配空间 ?但是类加载阶段不是先于运行阶段么 ?
这个分离的设计有助于节省内存和提高性能,因为不是所有类都会被实例化,只有在需要时才会分配内存。
但是对于类名, 方法名, 静态属性的符号引用替换为直接引用是没有这个问题的。
执行类的静态初始化。在类首次被主动使用(例如,创建对象实例、访问静态字段或调用静态方法)时触发。
在这个阶段,虚拟机会初始化类中的静态字段和静态代码块,确保它们按照指定的顺序被正确地初始化。
此时上述的代码:
public static int value = 123;
此时 value 的值才是 123.