关于类加载器

关于类加载器

一直想写一篇关于类加载的东西,嗯!就今天吧(持续更新)

java作为天生可以动态扩展的语言,其特性就是依赖运行期动态加载和动态链接这个特点实现,比如一个接口,这个接口可以在运行时再指定其实际实际类,用户可以通过java预定义的和自己定义类加载器让一个本地应用程序可以在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分,这种组装被广泛运用,Applet,JSP,OSGi都是用java语言运行期类加载的特性.

类加载时机

类加载时机如下图
avatar
如图类加载总共分为7个阶段,其中验证、准备、解析三个部分统称为链接,类的加载过程必须按照这种顺序按部就班的“开始”,请注意是开始不是“进行”或者“完成”,因为在类的解析阶段是有可能在初始化之后进行的,这是为了支持java语言运行时绑定(动态绑定或晚期绑定)

  • 类什么时候被加载?
    • 类什么时候加载虚拟机规范中并没有严格规定,这点是虚拟机具体实现自由把控,但对于初始化阶段虚拟机规范则是严格规定有且仅有5种情况必须对类进行初始化,而初始化之前必须是类被加载、被验证、并且已准备,
      • 当遇到new,getstatic,putstatic或者invokestatic 这个四个字节码指令时,如果类没有进行过初始化,则需要先进行初始化,这四条指令最常见的场景是 new Object() 和读取或者设置一个静态属性时(被final修饰,以及已经在编译期把结果放入常量池的静态字段除外)。
      • 使用反射时候。
      • 初始化类时候,如果发现其父类没有进行过初始化,则需要先触发其父类初始化。
      • 当虚拟机启动时,指定的一个类要执行的主类(包含main()方法那个类),虚拟机会先初始化这个类。
      • 当使用jdk动态语言支持时候,如果一个java.lang.invoke.MethodHandle实例最后解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄并且句柄多对应的类没有进行初始化,则需要先触发其初始化。
        上面五种场景的行为称为对一个类进行主动引用,除此之外,所有引用类的方式都不会触发其初始化,称之为被动引用。下面分别举三个被动调用例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.me.test;



public class SuperClass {

static {
System.out.println("superclass !");
}
public static int value = 1;
public static class ChildClassA extends SuperClass {
static {
System.out.println("child");
}
}
/**被动动使用类字段引用例子*/
public static void main(String[] args) {
/**
* 这里只会输出 “superclass” 而不会输出 "child",对于静态字段,
* 只有直接定义这个字段的类才会被初始化,至于是否触发子类的初始化
* 之前的步骤我虚拟机规范并未明确确定,我们可以使用 -XX:+TraceClassLoading
* 参数观察到此操作会导致子类的加载详情
*/
System.out.println(SuperClass.value);
}

public static void main(String[] args) {
SuperClass sca = new SuperClass[10];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SuperClass {
static {
System.out.println("superclass !");
}
public static int value = 1;
}

public class ChildClassB extends SuperClass {
static {
System.out.println("ChildClassB");
}
}
/**注意这里没有使用静态内部类而是另外创建一个class 在一个class里面nwe 关键字是会初始化类*/
public class NotInitialization {
public static void main(String[] args) {
/**
* 通过数组定义来引用类,不会触发此类初始化!
* 但是这里初始化另外一个类[Lcom.me.test.SuperClass,对用户啊来说这是个不合法的类
* 它是由虚拟机创建直接继承Object的子类,这个类表示为一个SuperClass 的一维数组
*/
SuperClass[] sca = new SuperClass[10];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ConstClass {
static {
System.out.println("ConstClass");
}
public static final String CONST_CLASS="constClass";
}
public class NotInitialization {
public static void main(String[] args) {
/**
* 常量在编译阶段会存入调用类的常量池中,本质上并没有直接饮用到定义的常量类
*/
System.out.println(ConstClass.CONST_CLASS);
}
}