class code is loaded at the point of first use.
- 1. the first object of that class is constructed.
- 2. static field or static methods is accessed.
- 3. static initialization block take places. (按照在class definition中书写的顺序(texture order)依次load)
class load 和初始化的顺序:
1. class A loaded.
2. base class B will be found and loaded if A has one.
3. base class C will be found and loaded if B has one.
4. root class C static initialization.
5 class B static initialization.
6. class A static initialization.
7. root class C constructor called.
8. primitives in object are set to 0 & object references in this object are set to null.
9. class C instance values are initialized in texture order.(like this: private int i = 9;) & rest of C constructor.
10. B preformes 7 - 9
11. A performes 7 - 9
恩,想了想,这里还是添加一下具体的解释:
part 1 加载
当我们使用class A 的时候,根据class code is loaded at the point of first use 的原则,class A会被加载到jvm中。这时候,如果jvm发现A有超类B,那么就会去需找B并加载到jvm中,同样如果B还有超类C,那么超类C也会被找到并load。我们知道,所有java类都有一个默认的超类Object,那么最终Object类会被load到jvm中。
因此,class load部分的顺序是:A - B - C - Obejct
part 2 静态变量初始化
所有的class加载完成后,就开始静态变量的实例化。这里首先从root class开始,即Object的静态变量初始化,然后执行C的静态变量的初始化,然后是B,最后才是A。并且静态变量只会初始化一次,如果在超类中有变量static初始化,而子类继承了这个protected field,那么子类使用时不会再次初始化了。第二次建立对象的时候,不会再执行初始化static变量的代码了,这点需要注意一下!
也许比较难以理解,还是看个例子吧:
public class Insect {
protected static int i = 10;
public Insect() {
System.out.println("Insect.i = " + i);
}
}
public class Beetle extends Insect {
public Beetle() {
System.out.println("Beetle.i = " + i); // 这里会直接继承父类已经初始化完成的static field, 输出10
System.out.println("Beetle.i = " + i);
}
public static void main(String[] args) {
Beetle b = new Beetle();
}
}
public class Insect {
protected static int i = 10;
public Insect() {
System.out.println("Insect.i = " + i);
}
}
public class Beetle extends Insect {
private int i = 20;
public Beetle() {
System.out.println("Beetle.i = " + super.i); // 这里会直接继承父类已经初始化完成的static field, 输出10
System.out.println("Beetle.i = " + i); // 由于子类的private变量覆盖了父类的变量,因此这里访问到的是子类的i,输出20
}
public static void main(String[] args) {
Beetle b = new Beetle();
}
}
part 3 构造器调用
首先调用obejct的构造器,然后最先进行的是实例变量的初始化工作,即类似这样的表达式执行: private int i = 10;如果有多个,则按照texture order编码书写顺序依次执行,这部分工作完成之后,才开始构造器中的代码的真正执行。
然后是C - B - A按照前述的方法依次执行自己的构造器,最终A对象完成全部初始化工作。
如果下面的例子能够理解的话,这部分就算弄明白了
class Insect {
private int i = 9;
protected int j;
Insect(){
System.out.println("i = " + i + ",j = " + j);
j = 39;
}
private static int x1 = printInt("static Insetct.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();
}
}
/* Output:
static Insetct.x1 initialized. load完成,处于static阶段,会执行所有的static,包括main
static Beetle.x2 initialized.
Beetle constructor.
i = 9,j = 0 (i有在定义时赋予了初始值,而j没有被jvm自动初始化为0)
Beetle.k initialized. (这里才执行到了子类的构造函数)
k = 47
j = 39 (j从父类Insect中继承而来,父类构造函数已经将其初始化为39)
*/