细说JavaJava变量初始化顺序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细说JavaJava变量初始化顺序相关的知识,希望对你有一定的参考价值。

  Java的变量初始化顺序,对这里一直似懂非懂,面试的时候也经常被问到,但答的一直不好,现在整理记录一下,以后忘记了可以来看看。

  程序分为两个部分,第一个部分不考虑继承,第二个部分考虑继承;

(1)不考虑继承的情况

代码如下:

public class JavaTest {
    public JavaTest() {
        System.out.println("执行JavaTest构造方法1");
    }

    public JavaTest(String param) {
        System.out.println("执行JavaTest构造方法2");
    }

    static {
        System.out.println("JavaTest静态代码块1");
    }

    {
        System.out.println("JavaTest代码块1");
    }

    private static int max1 = getMax1();
    private int min1 = getMin1();

    public int getMin1() {
        System.out.println("初始化成员变量min1");
        return 0;
    }

    public static int getMax1() {
        System.out.println("初始化静态成员变量max1");
        return 0;
    }

    static {
        System.out.println("JavaTest静态代码块2");
    }

    {
        System.out.println("JavaTest代码块2");
    }

    private static int max2 = getMax2();
    private int min2 = getMin2();

    public int getMin2() {
        System.out.println("初始化成员变量min2");
        return 0;
    }

    public static int getMax2() {
        System.out.println("初始化静态成员变量max2");
        return 0;
    }

    public static void main(String[] args) {
        System.out.println("==============================");
        new JavaTest();
        System.out.println("==============================");
        new JavaTest("param");
    }
}

简单说一下:本实例中,共声明了两个静态代码块,两个初始化块,两个构造器,两个静态成员变量,两个非静态成员变量,并分散的声明开了,这个程序仅仅是为了做测试,来证明各种初始化方式的顺序,实际编写时切勿这样写,否则会使程序非常难于维护。

执行结果:

JavaTest静态代码块1
初始化静态成员变量max1
JavaTest静态代码块2
初始化静态成员变量max2
==============================
JavaTest代码块1
初始化成员变量min1
JavaTest代码块2
初始化成员变量min2
执行JavaTest构造方法1
==============================
JavaTest代码块1
初始化成员变量min1
JavaTest代码块2
初始化成员变量min2
执行JavaTest构造方法2

通过以上结果我们可以得出一些结论结论:

静态的初始化要先于实例的初始化,并且只执行一次。静态的初始化后,才开始为实例变量分配空间,执行初始化,最后执行构造器。

具体执行情况可以总结如下:

(1)在类加载时,为类中的静态成员变量分配内存空间,并初始化默认值;

(2)执行静态成员变量的初始化操作。而静态成员的初始化有两种方式:在声明时直接初始化与静态代码块。两种初始化方式会按照在类中出现的顺序(声明的顺序)来执行。

(3)上面两步只会在类加载时执行一次;

(4)如果创建了类的对象,便在堆中为类的实例分配内存空间,并初始化默认值;

(5)执行实例变量的初始化操作。同样有两种方式:声明时直接初始化与初始化块。这两种方式也是按照在类中出现的顺序来执行;

(6)执行类的构造器方法。

 

  注意:虽然类的成员变量可以在声明时为其变量直接初始化,但声明与初始化并不是同时执行的。对于静态成员变量,会先为所有类中声明的静态成员变量分配空间,每个变量存在默认值后,才会执行变量声明处的初始化,而这种初始化方式是静态初始化块按照在类中出现的先后顺序来执行的。对于实例变量,与静态变量是类似的。

  既然类的变量空间分配是先于初始化执行的,那么就存在这样一种情况,在变量创建之后,而在变量的初始化之前。如果在此期间使用此变量,就可能得不到我们想要的结果。

用一个小例子说明一下:

public class Test2 {
    {
        print();
    }
    
    private int max = 9;
    private String maxValue;

    public String getMax() {
        return maxValue;
    }

    public void print() {
        maxValue = "max: " + max;
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();
        System.out.println(test2.getMax());
    }
}

大家看一下打印结果就知道了:

max: 0

  正常情况下,应该能打印出9的,但却打印出了0。问题就在于对print的调用,因为该方法是在初始化块中调用的,而初始化块与实例变量在声明处的初始化是同等级的,会按照类中出现的顺序执行;

  程序中初始化块出现在max变量的前面,所以会在max变量初始化前执行,在调用print方法时,max变量虽然已经声明,但却尚未执行初始化,其默认值为0,所以打印的结果就是0;

 

(2) 继承情况下的初始化顺序

 

以上是关于细说JavaJava变量初始化顺序的主要内容,如果未能解决你的问题,请参考以下文章

细说JavaJava的重写与隐藏

JAVAjava中char类型数组用数组名打印结果不是地址值而是数组内容

javajava 局部变量表中的槽是可以重用的

JavaJava中的final关键字和static

精通awk系列(14):细说awk中的变量和变量赋值

java 静态代码块 代码块 构造函数 静态成员变量 成员变量的初始化顺序