干翻95%程序猿的一道题 - 精通类初始化、程序执行过程

点进来,你就是上进、乐于挑战的青年,你会成为Top5的。话不多说,开干!

    

问:说说下面程序的输出是什么?

在看代码之前,先简单说一下程序是怎么运行起来的,这样更有利于大家解题:

1、输入“java Test”命令,企图运行Test类的main方法,发现Test类没加载到内存中

2、加载、链接、初始化Test类。这里加载、链接两个概念不明白不要紧,不影响我们解题。先说说“初始化”的概念,因为从类初始化这步开始涉及java代码的执行

初始化概念:初始化是指执行类或接口的初始化方法。这里的方法,大家先简单理解为虚拟机自己生成的一个方法,在初始化阶段被JVM执行,具体的来龙去脉后续会解释。

初始化规则:static变量初始化代码和static代码块中的代码,在类初始化这步按照在代码中书写的顺序被执行,且 类初始化前,其直接超类已经被初始化,直接超类的直接超类也需如此。

3、运行Test类的main方法

有了前面知识点的铺垫,大家看看下面的代码吧:    

/**
* 通过本程序来理解类的初始化和程序执行过程
* @create 2019-10-19-16:08
*/

public class ClassInitSequenceTest extends Father {

    static int si = 2;

    int j =3;

    static{
        System.out.println("1 ClassInitSequenceTest static block");//1
    }

    {
        System.out.println("2 ClassInitSequenceTest instance block");//2
    }

    ClassInitSequenceTest(){
        System.out.println("3 ClassInitSequenceTest constructor");//3
    }

    public static void main(String[] args) {
        new ClassInitSequenceTest();
    }
}


class Father{

    int i = 1;

    static ClassInitSequenceTest t = new ClassInitSequenceTest();

    static{
        System.out.println("4 father static block");//4
    }

    {
        System.out.println("5 father instance block");//5
    }

    Father(){
        System.out.println("6 father constructor");//6
    }

}

程序输出如下: 


5 father instance block
6 father constructor
2 ClassInitSequenceTest instance block
3 ClassInitSequenceTest constructor
4 father static block
1 ClassInitSequenceTest static block
5 father instance block
6 father constructor
2 ClassInitSequenceTest instance block
3 ClassInitSequenceTest constructor

大家可能有疑惑,没关系。看根据javac编译后的字节码翻译的代码,这样大家就会明白了:

class ClassInitSequenceTest extends Father {

    static int si;

    int j;

    /**
     * 特殊的方法,由JVM进行“类初始化阶段”调用
     */

    void (){
        si = 2;
        System.out.println("1 ClassInitSequenceTest static block");//1
    }

    ClassInitSequenceTest(){
        super();
        j = 3;
        System.out.println("2 ClassInitSequenceTest instance block");//2
        System.out.println("3 ClassInitSequenceTest constructor");//3
    }

    public static void main(String[] args) {
        new ClassInitSequenceTest();
    }
}

class Father{

    int i;
    static ClassInitSequenceTest t ;
    static{
        System.out.println("4 father static block");//4
    }
    /**

     * 特殊的方法,由JVM进行“类初始化阶段”调用

     */
    void (){
        t = new ClassInitSequenceTest();
        System.out.println("1 ClassInitSequenceTest static block");//1
    }

    Father(){
        super();
        i = 1;
        System.out.println("5 father instance block");//5
        System.out.println("6 father constructor");//6
    }
}

翻译的程序中有一个知识点没有反应出来,那就是:类初始化前,其直接超类已经被初始化,直接超类的直接超类也需如此。

这里把知识点做个总结,翻译的代码中蕴含的知识点如下:

1、javac编译实现:

   1.1、 javac编译器生成一个方法,把“静态变量的初始化语句”和“static代码块语句”按照代码中的顺序编译到方法中;

    1.2、javac编译器把“实例变量的初始化语句”和“实例代码块的语句”按照代码中的顺序编译到了构造方法中;

以上两点从翻译的代码中可以看出来。

2、类初始化:类初始化前,其直接超类已经被初始化,直接超类的直接超类也需如此。即javac生成的方法由JVM在“类初始化阶段”调用,调用类的方法前,必须先调用父类的方法。

3、对象实例化 - 执行构造方法在堆中创建一个对象。在没有显示调用父类构造方法情况下,所有构造方法的首行都会隐含调用父类的无参构造方法。

你可能感兴趣的:(JVM实战高手)