一道题测试java学的咋样。此前就已经看过这道题,但是刚看了一下还是不会,走马观花,不得其理。

# 题目

这道题涉及到两个部分,一个是类初始化的过程,一个是实例初始化的过程,如果没有搞清楚这两个问题,这题还是有点难度的。

题目的代码是这样的,有一个父类有一个子类,求执行代码输出的结果。

package org.example;
public class Father {
    private int i = test();
    private static int j = method();
    static {
        System.out.print("(1)");
    }
    Father(){
        System.out.print("(2)");
    }
    {
        System.out.print("(3)");
    }
    public int test(){
        System.out.print("(4)");
        return 1;
    }
    public static int method(){
        System.out.print("(5)");
        return 1;
    }
}
package org.example;
public class Son extends Father{
    private int i = test();
    private static int j = method();
    static {
        System.out.print("(6)");
    }
    Son(){
        System.out.print("(7)");
    }
    {
        System.out.print("(8)");
    }
    public int test(){
        System.out.print("(9)");
        return 1;
    }
    public static int method(){
        System.out.print("(10)");
        return 1;
    }
    public static void main(String[] args) {
        Son s1 = new Son();
        System.out.println();
        Son s2 = new Son();
    }
}

# 分析

不妨思考几分钟,结果是什么,也许会出乎意料。

代码中涉及了好几个部分例如静态属性,静态方法,静态代码块,代码块等等,要搞明白各部分哪些应该在类初始化执行,哪些应该在实例初始化执行。

假如现在先把子类主函数中的代码删掉,这就相当于类的初始化了。

Son s1 = new Son();
System.out.println();
Son s2 = new Son();

然后运行代码,结果为 (5)(1)(10)(6)

说明这部分就是进行了类初始化,类初始化执行了<clinit>()方法,那么它执行了哪些方法?由静态类变量显示赋值代码和静态代码块组成。

并且类变量显示赋值代码和静态代码块从上到下顺序执行,需要注意<clinit>()方法只执行一次。

子类初始化,父类要先于子类初始化。回归到代码中,父类要先开始初始化,执行循序如下:

(1) j = method();
(2) 父类的静态代码块

然后再进行子类初始化:

(1) j = method();
(2) 子类静态代码块

这样正好可以得到结果(5)(1)(10)(6)

然后再把刚才主函数中删除的代码添加回来,下一步就要进行实例的初始化了。实例初始化实际上就是执行了<init>()方法,实例初始化又执行了哪些方法呢?实例初始化可以执行多次,创建一个实例则执行一次,而类初始化仅在程序运行开始时执行一次。

<init>()方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成,子类实例化,父类同样也要先于子类,在子类的构造方法的第一行代码默认有一个super(),简单来说,就是先要执行父类的构造方法。其中,super()最先开始执行,非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行。

按照这个方法,在回归到代码中。子类实例化的顺序:

(1) super();
(2) i = test();
(3) 子类的非静态代码块
(4) 子类的无参构造

父类实例化的过程:

(1) super();
(2) i = test();
(3) 父类的非静态代码块
(4) 父类的无参构造

这里还是会涉及到方法重写的一个问题,其实父类i = test()执行的是子类重写过的test()方法,神奇。

要搞清楚这个问题,首先要补充一个概念,什么叫对象的多态性。

所谓对象多态性指的是,非静态方法前面实际上有一个默认的对象thisthis在构造器它表示的是正在创建的对象,因为此时正在创建子类Son的实例对象,因此这个this便代表的是子类Son

所以就能够解释为什么父类执行的是子类重写过的test()方法了。

这样分析下来结果也就显而易见了。

(5)(1)(10)(6)(9)(3)(2)(9)(8)(7)
(9)(3)(2)(9)(8)(7)

# 参考连接

java经典面试题之类初始化和实例初始化 (opens new window)