目录
1.jvm如何加载一个类?
JVM加载类的过程
Java虚拟机(JVM)加载类的过程涉及多个步骤,这些步骤共同确保了类的正确加载和初始化。以下是类加载的主要步骤:
-
加载(Loading):
- 加载阶段的任务是通过类的全限定名找到该类的二进制字节流,并将其转换为方法区中的运行时数据结构。
- 同时,JVM在内存中为该类创建一个
java.lang.Class
对象,作为访问该类的入口。
-
验证(Verification):
- 验证阶段确保类文件的字节流符合Java虚拟机规范,不会威胁到虚拟机的安全。
- 验证包括文件格式验证、元数据验证、字节码验证和符号引用验证。
-
准备(Preparation):
- 准备阶段为类变量分配内存,并将其初始化为默认值(对于静态变量,默认值通常是0或null)。
-
解析(Resolution):
- 解析阶段将常量池中的符号引用转换为直接引用,即将符号形式的类、接口、字段和方法引用转换为内存中的直接地址。
-
初始化(Initialization):
- 初始化阶段是执行类构造器
<clinit>()
方法的过程,在这个阶段,类变量被赋予具体的值,静态代码块被执行。
- 初始化阶段是执行类构造器
-
使用和卸载(Use and Unloading):
- 类被加载和初始化后,就可以在JVM中使用了。当类不再被使用,并且满足卸载条件时,它的内存会被释放。
双亲委派模型
JVM采用双亲委派模型来组织类加载器的工作。在这个模型中,类加载器在尝试加载一个类之前,会委托给其父类加载器进行加载。只有当父类加载器无法加载该类时,子类加载器才会尝试自己加载。这个模型有助于确保类的唯一性和防止类的重复加载。
自定义类加载器
在某些情况下,可能需要自定义类加载器来满足特定的类加载需求,例如动态加载类或加载加密的类文件。自定义类加载器通常继承自java.lang.ClassLoader
类并重写其loadClass()
方法。
2.jvm如何初始化一个类
JVM类初始化过程
在Java中,类的初始化是指为类分配内存并准备类数据的过程。这个过程发生在以下几种情况下:
- 当尝试创建类的实例时,即使用
new
关键字。 - 当尝试访问类的静态变量或设置静态变量的值时,如果这些静态变量尚未被初始化。
- 当调用类的静态方法时。
- 当使用
java.lang.reflect
包的方法对类进行反射调用时。 - 当初始化一个类时,如果其直接父类还没有被初始化,则先初始化其直接父类。
- 当JVM启动时,用户指定的主类会被初始化。
类的初始化过程包括以下步骤:
- 类加载:将类的
.class
文件加载到JVM的内存中,并为其创建java.lang.Class
对象。 - 验证:确保类文件符合Java虚拟机规范,确保安全。
- 准备:为类变量分配内存并设置默认初始值。
- 解析:将类的符号引用转换为直接引用。
- 初始化:执行类构造器
<clinit>()
方法,包括静态变量的赋值和静态初始化块的执行。
在初始化过程中,如果类有父类,JVM会确保父类在子类之前被初始化。此外,如果类中存在静态初始化代码,这些代码会按照它们在源代码中出现的顺序依次执行。
初始化是类加载过程中的一个重要环节,它确保了类在使用前处于一个一致和有效的状态。在编写Java程序时,了解类的初始化时机和过程对于避免潜在的错误和提高程序的稳定性至关重要.
3.剖析类始化
Java类初始化的详细过程
Java类的初始化是一个关键的生命周期阶段,它确保了类在使用前被正确设置。类初始化包括以下步骤:
-
加载(Loading):
-
类加载器负责将类的.class文件从文件系统或者其他来源加载到JVM中。
-
加载过程还包括为类创建
java.lang.Class
对象的内存区域。
-
-
验证(Verification):
- 验证确保加载的类文件符合Java虚拟机规范,不含有安全风险。
-
准备(Preparation):
- 在这一阶段,类变量(静态变量)被分配内存,并初始化为默认值(如0、null或false)。
-
解析(Resolution):
- 解析阶段将类、接口、字段和方法的符号引用转换为直接引用。
-
初始化(Initialization):
-
初始化阶段是类初始化的核心,它执行静态变量的赋值和静态初始化块中的代码。
-
<clinit>()
方法是由编译器自动合成的,包含了静态变量的赋值和静态初始化代码块的执行顺序。
-
-
使用(Use):
- 一旦类被初始化,它就可以被实例化、使用其静态方法和访问其静态变量。
-
卸载(Unloading):
- 当类不再被使用,并且满足卸载条件时,它的类定义和元数据将被垃圾回收器清理。
类初始化的触发条件
类初始化可以由多种情况触发,包括但不限于:
-
创建类的实例(使用
new
关键字)。 -
访问类的静态变量或设置静态变量的值。
-
调用类的静态方法。
-
直接使用
java.lang.reflect
包中的方法进行反射调用类。 -
初始化一个类的子类(此时父类必须先被初始化)。
-
当虚拟机启动时,用户指定的主类会被初始化。
父类与子类的初始化顺序
在涉及继承的场景中,父类必须在子类之前被初始化。这意味着,如果子类的初始化触发了父类的初始化,父类的静态变量和静态初始化块将先于子类的相应部分被处理。实例化过程中,子类的构造方法会在父类的构造方法之后被调用,确保了父类的构造逻辑先执行.
注意事项
-
静态变量(包括
static final
变量)在类加载和准备阶段就已经被初始化,因此访问静态变量通常不会触发类的初始化。 -
如果子类只访问了父类中的
static final
变量,不会触发父类的初始化,因为这些变量在编译时就已经确定并放入常量池。 -
使用
Class.forName()
方法会触发类的初始化,因为它涉及到类的加载和链接。