定义:当程序主动使用某个类时,如果该类还没被加载到内存中,那么系统会通过加载、连接、初始化这三个步骤对该类进行初始化,JVM通常将会连续完成这三个步骤,所以一般情况下这三个步骤统称为类的加载。
实际上类的加载就是将类的class文件读进内存,并为之创建一个java.lang.Class对象。
类的初始化
当类被加载之后,系统会生成一个对应的Class文件,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。之后进入类的初始化阶段,初始化阶段JVM负责对类进行初始化,主要是对类变量进行初始化,两种方式对类变量进行初始化①声明类变量时指定初始化值②是用静态代码块指定初始化值。
类的初始化包含以下几个步骤:
①如果类还没被加载和连接,先进行加载、连接
②如果类的直接父类还没被初始化则先初始化直接父类,类推到Object类,所以Object类永远是最先被初始化的类
③系统会依次执行初始化语句。(即前边的初始化值有可能在后面被覆盖)
/**
* 男人类继承自人类
*/
class Man extends Person{
static{
b = 100;
}
private static int b = 2;
private static int c = 3;
private static int d = 4;
private static int e;
static {
d = 200;
}
public static void main(String[] args) {
System.out.println("a = " + Person.a);
System.out.println("b = " + Man.b);
System.out.println("c = " + Man.c);
System.out.println("d = " + Man.d);
System.out.println("e = " + Man.e);
}
}
/**
* 人类
*/
class Person {
static int a = 1;
}
#输出
a = 1
b = 2
c = 3
d = 200
e = 0
类初始化的时机
当Java程序首次通过下面6个方式使用某个类或接口的时候,会初始化此类或接口。
①创建类的实例。包括new一个实例,反射创建实例,反序列化创建实例。
②调用某个类的静态方法(类方法)
③访问类或者接口的类变量,或者为类变量赋值
④使用反射强制创建某个类或接口的Class对象,例如Class.for("Person")若系统还没初始化Person类,这行代码会初始化Person类,并返回Person类对应的Class对象。
⑤初始化某个类的子类,该子类的所有父类都会被初始化。
⑥直接使用java.exe运行某个主类。
注意:当类变量被final修饰的时候,如果类变量的值在编译时候能确定,在使用该类变量的时候不会初始化该类,因为相当于使用了一个常量。如下:
import java.time.LocalDateTime;
public class finalTest {
public static void main(String[] args) {
System.out.println(Dog.b);
System.out.println(Cat.a);
}
}
class Cat{
static{
System.out.println("初始化Cat类");
}
/**
* 编译时候确定了a的值
*/
final static String a = "1000";
}
class Dog{
static{
System.out.println("初始化Dog类");
}
/**
* 编译时并没有确定b的值
*/
final static String b = LocalDateTime.now().toString();
}
#输出
初始化Dog类
2019-11-02T11:22:18.823
1000
另一注意点:当使用ClassLoader类的loadClass()来加载某个类时候,并不会初始化该类,使用Class的forName()静态初始化方法才会强制初始化该类。
类加载器
类的加载由类加载器完成,类加载器通常由JVM提供,JVM提供的类加载器是系统类加载器,其次,开发者可以通过集成ClassLoader类来创建自己的类加载器。
当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:
①Bootstrap ClassLoader: 根类加载器
②Extension ClassLoader: 扩展类加载器
③System ClassLoader: 系统类加载器
JVM的类加载机制主要有以下三种:
①全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖和引用的其他Class也将由该类加载器负责载入。
②父类委托:先由父类加载器试图加载该Class,只有当父类加载器无法加载该类时,才会从自己的类路径中加载该类。
③缓存机制:保证所有的加载过的Class都会被缓存,当程序需要使用某个Class时,类加载器先从缓存区中查找,只有在缓存区不存在该Class对象时,系统才会读取该类对应的二进制文件,转换成Class对象,存入缓存区。
类加载器加载Class大致要经过以下几个步骤:
①在缓存区检查该Class是否载入过,如果有则直接第⑧步,否则执行第②步。
②如果父类加载器不存在(如果父类加载器不存在,则parent一定是根加载器,或者本身就是根加载器,因为根加载器不是java实现的,所以返回null)直接跳第④步,如果父类加载器存在,则执行第③步。
③请求使用父类加载器去载入目标类,如果成功载入则跳第⑧步,否则执行第⑤步
④请求使用根类加载器载入目标类,如果成功载入则跳第⑧步,否则跳第⑦步
⑤当前类加载器尝试寻找Class文件(从与此加载器相关的类路径中寻找)如果找到执行第⑥步,找不到执行第⑦步
⑥从文件中载入Class,成功载入跳第⑧步
⑦抛出ClassNotFoundException异常
⑧返回对应的java.lang.Class对象
网友评论