浅谈Java中各成员变量初始化流程
前言
最近在看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 constructori=11 j=0Son constructorI2=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类。
以上就是浅谈Java中各成员变量初始化流程的详细内容,更多请关注其它相关文章!