JVM如何在Java中加载父类

K.T*_*ess 5 java inheritance initialization classloader

码:

class A {
    static {
        System.out.println("loading A static 1");
    }
    static {
        System.out.println("loading A static 2 B.c= "+B.c);        
    }
    static {
        System.out.println("loading static 3");
    }
    static int a=10; 
    A(){        
    }
}

class B extends A{
    static {
       System.out.println("loading B A.a= "+A.a);
    }
    static int c = 50;
}
public class Test {
    public static void main(String[] args) {
        new B();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

loading A static 1
loading A static 2 B.c= 0
loading static 3
loading B A.a= 10
Run Code Online (Sandbox Code Playgroud)

从这个出来可以说,父类在子类之后加载,但子类在父类之后初始化?如果是这样,JVM如何加载类层次结构?

Sot*_*lis 7

Java语言规范解释了初始化类的过程.

类或接口类型T将在第一次出现以下任何一个之前立即初始化:

  • T是一个类,并且创建了T的实例.

  • T是一个类,并且调用由T声明的静态方法.

  • 分配由T声明的静态字段.

  • 使用由T声明的静态字段,该字段不是常量变量(第4.12.4节).

  • T是顶级类(第7.6节),并且执行在词典内嵌套在T(第8.1.3节)内的断言语句(第14.10节).

提供更多细节

[...]

在C的初始化锁LC上同步.这涉及等到当前线程可以获取LC.

[...]

如果C的Class对象指示当前线程正在为C进行初始化,那么这必须是初始化的递归请求.释放LC并正常完成.

[...]

接下来,如果C是类而不是接口,并且其超类SC尚未初始化,则递归地执行SC的整个过程.如有必要,请先验证并准备SC.如果SC的初始化由于抛出异常而突然完成,则获取LC,将C对象标记为错误,通知所有等待线程,释放LC,并突然完成,抛出因初始化SC而导致的相同异常.

所以

new B();
Run Code Online (Sandbox Code Playgroud)

要求B初始化类.因为B是子类A,A需要初始化.在初始化时A,这个

static {
    System.out.println("loading A static 2 B.c= "+B.c);        
}
Run Code Online (Sandbox Code Playgroud)

表示B需要初始化,但B已经处于初始化过程中,因此暂时忽略它并A继续初始化.由于B初始化未完成,因此该字段c尚未初始化为50,因此会打印0.

A初始化完成.B初始化继续.B初始化完成和轰隆!你完成了.


Kep*_*pil 5

您可以通过执行以下命令进行检查java -verbose Test

...
[Loaded A from file:/.../src/main/java/]
[Loaded B from file:/.../src/main/java/]
loading A static 1
loading A static 2 B.c= 0
loading static 3
loading B A.a= 10
...
Run Code Online (Sandbox Code Playgroud)

所以不,父类也首先加载。