代码:
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();
}
}
输出:
loading A static 1
loading A static 2 B.c= 0
loading static 3
loading B A.a= 10
从这个出来,我们可以说父类在子类之后加载但子类在父类之后初始化吗? 如果是这样,JVM如何加载类层次结构?
答案 0 :(得分:5)
Java语言规范explains初始化类的过程。
类或接口类型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类的Class对象标记为错误,通知所有 等待线程,释放LC,并突然完成,抛出相同的 初始化SC导致的异常。
所以
new B();
要求初始化类B
。由于B
是A
的子类,因此需要初始化A
。在初始化A
时,这个
static {
System.out.println("loading A static 2 B.c= "+B.c);
}
表示B
需要初始化,但B
已经在初始化过程中,因此暂时忽略它,A
初始化继续。 由于B
的初始化未完成,因此字段c
尚未初始化为50,因此会打印0
。
A
初始化完成。 B
初始化仍在继续。 B
初始化完成并繁荣!你已经完成了。
答案 1 :(得分:4)
您可以通过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
...
所以不,首先加载父类。
答案 2 :(得分:1)