在以下Java代码中,Insect类由Beetle类继承。
class Insect {
private int i = 9;
protected int j;
Insect() {
System.out.println("i = " + i + ", j = " + j);
j = 39;
}
private static int x1
= printInit("static Insect.x1 initialized");
static int printInit(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
private int k = printInit("Beetle.k initialized");
public Beetle() {
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2
= printInit("static Beetle.x2 initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
}
输出以某种方式令人着迷。
static Insect.x1已初始化
静态Beetle.x2初始化
甲壳虫构造函数
i = 9,j = 0
Beetle.k初始化
k = 47 j = 39
此特定示例中的初始化顺序是什么?
为什么在x1之后立即初始化x2?为什么主方法的第一行(System.out.println("Beetle constructor");
)在 x1和x2 intializaton之后执行。好令人兴奋。
我知道派生类构造函数会自动调用基类构造函数(除非它有参数,你使用的是super关键字)。我知道变量在构造函数之前被初始化,静态变量在其他变量之前被初始化。
答案 0 :(得分:4)
x1
和x2
是静态的。这意味着它们在加载类时被初始化。由于main方法位于Beetle
,因此必须在调用main
之前加载该类。这就是为什么x1
和x2
初始化是您看到的第一件事。不确定为什么这两个的顺序是这样的。
现在您正在调用main
并执行System.out.println("Beetle constructor");
。然后它调用Beetle()
,它首先隐式调用super()
(又名Insect()
)。这会打印i = 9, j = 0
,因为i
为9且j
尚未在该点初始化,这意味着它具有默认的int值0。
现在j
设置为39,流程在Beetle()
继续。现在,这会根据您的Beetle
初始化k
字段。因此,当涉及到Beetle()
中的显式代码时,超级构造函数会将k
初始化为47并将j
初始化为39。
答案 1 :(得分:2)
这可以解释如下 -
由于静态成员是非实例成员,即在该类的所有实例中只共享该成员的一个副本,因此首先初始化这些成员。
在这种情况下,首先初始化x1,因为它的静态成员存在于main方法所在类的基类中。
因此,第一个x1被初始化。
子类中的静态成员x2是由于相同的原因,也是因为昆虫类中没有其他静态成员。
从主方法打印甲壳虫构造函数。
出于显而易见的原因,一个接一个地调用超类和子类的构造函数
答案 2 :(得分:2)
JVM(类加载器)在调用其主静态方法时加载Beetle类。在类加载之后,将发生Beetle类的初始化,这意味着初始化类的所有静态成员。
基类始终是隐式初始化的,因此您可以看到在x2之前初始化的x1。
你会看到" Beetle构造函数"在x1和x2之后打印,因为当你引用类的静态方法时(正如你通过调用main那样),JVM执行顺序跳转到初始化类静态成员,然后继续执行main()方法
作为实验,请尝试将main方法移除到另一个类
public class Beetle1 {
public static void main(String[] args) {
System.out.println("Beetle1 constructor");
}
}
现在由于未引用Beetle构造函数,类加载器不会加载它,您将看到打印Beetle1构造函数。