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如何加载类层次结构?
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初始化完成和轰隆!你完成了.
您可以通过执行以下命令进行检查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)
所以不,父类也首先加载。