Java 类加载机制

Java 类加载机制探究。

类的加载、链接和初始化

系统有可能在第一次使用到某个类的时候加载某个类。也有可能采用预加载机制来加载某个类。

JVM 和类

不管一个 Java 程序如果复杂,都是运行在 JVM 上,而一个 JVM 就是操作系统中的一个 JVM 进程。

JVM 进程的中止可能有一下几种情况:

(1) 程序运行到最后正常结束。

(2)程序运行到使用 System.exit()Runtime.getRUntime().exit() 代码处结束程序。

(3)程序执行过程中遇到未捕获的异常或错误而结束。

(4)进程从外部被终止,比如人为结束进程,或者操作系统结束进程。

类的加载

类的加载指的就是将类的 .class 文件读入内存,并为之创建一个 java.lang.Class 对象。

类的加载是由类加载器来完成的。类加载器通常由 JVM 提供,但是程序员也可以自定义类加载器。JVM 提供的类加载器通常称为系统类加载器。

类的连接

TODO

类的初始化

JVM 初始化一个类包含一下几个步骤:

(1)假如这个类还没有被加载和连接,则程序先加载并连接该类。

(2)假如该类的直接父类还没有被初始化,则先初始化其直接父类。

(3)假如类中有初始化语句,则系统依次执行这些初始化语句。

当执行第 2 个步骤的时候,系统对直接父类的初始化也遵循这三个步骤。如果该直接父类又有直接父类,则系统再次重复这三个步骤来先初始化这个父类,以此类推。所以 JVM 最先初始化的总是 java.lang.Object 类。

其中第 3 步中的初始化语句的执行顺序为:

(1)父类的静态代码块或父类的静态成员变量(类变量),按在代码中出现的先后顺序执行。

(2)当前类的静态代码块或当前类的静态成员变量(类变量),按在代码中出现的先后顺序执行。

(3)父类的普通代码块或父类的普通成员变量,按在代码中出现的先后顺序执行。

(4)父类的构造函数。

(5)当前类的普通代码块或当前类的普通成员变量,按在代码中出现的先后顺序执行。

(6)当前类的构造函数。

参考:Initializing Fields - The Java™ Tutorials - Oracle

类初始化的时机

6种类初始化的时机。

  1. 创建类的实例。
  2. 调用某个类的类方法(静态方法)。
  3. 访问某个类或接口的类变量,或为该类变量赋值。
  4. 使用反射方式强制创建某个类或者接口对应的 java.lang.Class 对象。
  5. 初始化某个类的子类。这个子类的父类也都会被加载。
  6. 直接使用 java.exe 来运行某个主类,会先初始化该主类。

但是有一些特殊情况:

(1)对于一个 final 型的类变量(静态变量),如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。Java 编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此即使其他类中使用 类名.静态变量 的形式使用这个类变量,这个类也不会被加载。因为编译的时候,所有用到 类名.静态变量 的地方都已经替换成实际的值了。

比如,对于下边这段代码:

1
2
3
4
5
6
7
8
9
class Test{
static final String var1 = "testString";
}

public class Demo{
public static void main(String[] args) {
System.out.println(Test.var1);
}
}

虽然 Demo 类中使用到了 Test.var1,但是 Demo 类中用到 Test.var1 的地方在编译时就被替换成实际值 testString 了。所以在用到 Demo 类的时候并不会导致 Test 类的加载。

注意这里的条件是“该类变量的值在编译时就可以确定下来”,必须满足这个条件才可以。

小结

注意类的加载类的初始化是需要进行区分的。

类加载器

常见问题

  1. exception - How can I solve “java.lang.NoClassDefFoundError”? - Stack Overflow

参考资料

  1. 《疯狂 Java 讲义》第3版 第18章. 类加载机制与反射
  2. Jvm 系列(一):Java 类的加载机制 - 纯洁的微笑博客
  3. 老大难的 Java ClassLoader 再不理解就老了 - 知乎
  4. JVM 类加载机制详解 - ImportNew
  5. Chapter 4. The class File Format - Java Virtual Machine Specification
  6. [一]class 文件浅析 .class文件格式详解 字段方法属性常量池字段 class文件属性表 数据类型 数据结构
  7. Java: Which of multiple resources on classpath JVM takes? - Stack Overflow【同名资源的加载顺序】
  8. Java依赖冲突高效解决之道 - 墨天轮
  9. 如何实现Java类隔离加载?-阿里云开发者社区
  10. Java 类隔离加载的正确姿势 - 知乎