0.前言
最近在看Thinking in java,关于类中的各成员初始化一直未曾关注,这里记录一下,如有错误,请指正:
1.继承与初始化
了解包括继承在内的初始化全过程,以对所发生的一切有个全局性的把握,是很有益的。
在Java中,每个类的编译代码都存在于它自己的独立的文件中(即class文件),该文件只在需要使用程序代码时才会加载。一般来说,“类的代码在初次使用的时候才加载”,这通常包含下面两种情况:
- 创建类的第一个对象的时候
- 访问static方法或字段时
注意:初次使用类时,也是static初始化发生之处,所有对象和代码均按照书写顺序进行初始化,定义为static的字段仅会被初始化一次。
废话不多说,详细如下:
public class Base {
//static字段
private static int I1 = printInt("Init I1");
//普通字段
private int i = 11;
protected int j;
public Base(){
System.out.println("Base constructor");
System.out.println("i="+i+" j="+j);
j = 40;
}
static int printInt(String str) {
System.out.println(str);
return 10;
}
}
public class Son extends Base{
private static int I2 = printInt("Init I2");
public Son() {
System.out.println("Son constructor");
System.out.println("I2="+I2+" j= "+j);
}
public static void main(String[] args) {
Son son = new Son();
}
}
看完这段代码,你觉得输出是什么呢?
先给出答案,在来分析:
Init I1
Init I2
Base constructor
i=11 j=0
Son constructor
I2=10 j= 40
分析:
程序首先从Son类的main方法入手,于是加载器开始启动并找出Son编译代码(即class文件)。在进行加载中发现Son还有父类Base,于是继续加载Base的编译代码(如果Base还有父类则继续向上执行),接下来,根基类的static字段初始化,因为子类可能会依赖基类成员能否被正确初始化,所以发生了Init I1
, 然后往下到了Son,Son类的static字段初始化,于是发生了Init I2
,到此所有的必要的类加载完成,可以开始初始化对象。看到Son son = new Son()
这行代码,准备调用Son的构造器,我们知道在继承关系中,子类的构造器中会调用super(),当然这里是隐式调用。这样又会回到父类中去,不过在完成构造器之前,父类(Base)中的所有普通字段(即非static字段)都会完成自己的初始化,所以会看到输出i=11 j=0
,接着来到子类(Son),和父类的执行过程一样,先完成普通字段的初始化,再调用构造器方法。
说了一大堆,整体流程如下:
- 从程序入口开始,加载该类(这里设为Z类),如有继承关系递归向上,直到根类(这里假设A类)。
- 完成A类的static字段初始化,递归向下,直到遇到Z类。
- 完成A类的普通域初始化,完成A类的构造器,递归向下,直到遇到Z类。
网友评论