欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  Java

浅谈Java中各成员变量初始化流程

程序员文章站 2022-04-16 15:28:02
...

前言

最近在看Thinking in java,关于类中的各成员初始化一直未曾关注,这里记录一下,如有错误,请指正:

1.继承与初始化

了解包括继承在内的初始化全过程,以对所发生的一切有个全局性的把握,是很有益的。

在Java中,每个类的编译代码都存在于它自己的独立的文件中(即class文件),该文件只在需要使用程序代码时才会加载。一般来说,“类的代码在初次使用的时候才加载”,这通常包含下面两种情况:

  1. 创建类的第一个对象的时候

  2. 访问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),和父类的执行过程一样,先完成普通字段的初始化,再调用构造器方法。

说了一大堆,整体流程如下:

  1. 从程序入口开始,加载该类(这里设为Z类),如有继承关系递归向上,直到根类(这里假设A类)。

  2. 完成A类的static字段初始化,递归向下,直到遇到Z类。

  3. 完成A类的普通域初始化,完成A类的构造器,递归向下,直到遇到Z类。

以上就是浅谈Java中各成员变量初始化流程的详细内容,更多请关注其它相关文章!