1.Go!!!

以下代码的运行结果是:👇👇👇

JavaSE面试题——类初始化 & 实例初始化_java

运行结果如下:👇👇👇

JavaSE面试题——类初始化 & 实例初始化_java_02

分析:

  1. 首先这道题它考查了:类的初始化、实例的初始化、方法重写、对象的多态性这些内容。
  2. 类的初始化:
  • 一个类要创建实例需要先加载并初始化该类(main方法所在的类要先进行加载和初始化)。
  • 一个子类要加载并初始化,需要先加载并初始化它的父类。
  • 一个类的初始化就是执行<clinit>()方法,<clinit>()方法由静态类变量显示赋值代码和静态代码块组成,同时<clinit>()方法只会执行一次,而静态类变量显示赋值代码和静态代码块的执行顺序是 从上到下顺序执行(谁在前面先执行谁)。

      3. 实例初始化:

  • 实例初始化就是执行<init>()方法,<init>()方法可能重载有多个,有几个构造器就有几个<init>()方法。
  • <init>()方法由非静态(实例变量)显示赋值代码和非静态代码块、对应的构造器组成。它们三者的执行顺序是:非静态(实例变量)显示赋值代码和非静态代码块 从上到下顺序执行(谁在前面先执行谁),而对应的构造器则在最后执行。
  • 每次创建实例对象,调用对应构造器,执行的就是对应的<init>方法。
  • <init>方法的首行是 super() 或 super(实参列表),即对应会先执行父类的<init>方法。

       4.哪些方法不可以被重写?

  • final修饰的方法、静态方法、private等子类中不可见的方法。

       5.对象的多态性

  • 子类如果重写了父类的某个方法,通过子类对象调用的一定是子类重写过的方法。
  • 非静态方法默认的调用对象是 this。
  • this对象在构造器或者说<init>方法中就是正在创建的对象。
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;
    }
}
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;
    }

    //main方法所在的类需要先加载和初始化
    public static void main(String[] args) {
        Son s1 = new Son();
        System.out.println();
        Son s2 = new Son();
    }
}

这里先进行的是类初始化过程。

因为main方法在Son子类中,所以这里会先进行Son类的加载和初始化,而Son有一个父类Father,所以这里先对Father类进行加载和初始化。

先去执行Father类中的静态类变量显示赋值代码和静态代码块,因为静态类变量显示赋值代码 private static int j = method(); 位置更靠前,所以它先执行,而静态方法是不能被重写的,所以这里执行父类自己的method方法,首先输出(5)。然后继续执行父类的静态代码块,输出(1)。到此,父类的加载和初始化完成,转回子类进行加载和初始化。那么同父类一样,因为静态类变量显示赋值代码 private static int j = method(); 位置更靠前,所以它先执行,输出(10)。然后执行子类的静态代码块,输出(6)。即类初始化全部完成,目前输出了 (5)(1)(10)(6)。

下面进行实例初始化过程。

因为<init>()方法的首行是 super(),所以此时仍然是先到父类中进行实例初始化:非静态(实例变量)显示赋值代码和非静态代码块,而非静态(实例变量)显示赋值代码位置更靠前,private int i = test(); 它先执行,而在非静态方法里面都有一个默认的对象this表示当前正在创建的对象,因为第一次实例初始化过程是由main方法中的 Son s1 = new Son(); 走过来的,所以当前this对象指的就是Son对象,也就是执行的其实应该是子类重写之后的test方法,即输出(9)。然后继续执行父类的非静态代码块,输出(3)。最后执行父类的无参构造,输出(2)。到此,父类的实例初始化完成,转回子类进行实例初始化。同父类一样,因为非静态(实例变量)显示赋值代码位置更靠前,private int i = test(); 它先执行,输出(9)。然后执行子类的非静态代码块,输出(8)。最后执行子类的无参构造,输出(7)。即目前输出了 (5)(1)(10)(6)(9)(3)(2)(9)(8)(7)。

main中还有最后两行需要执行:System.out.println();   Son s2 = new Son();

首先打印一个换行,然后Son类new了第二个对象,要进行了第二次实例初始化(类初始化只会进行一次),所以类初始化相关的静态类变量显示赋值代码和静态代码块不会再执行了,只会执行非静态(实例变量)显示赋值代码和非静态代码块。那么同上面分析的一样,先去父类进行实例初始化、然后回到子类进行实例初始化。最终第二次实例初始化完成:(9)(3)(2)(9)(8)(7)。

即最终的结果就是:👇👇👇

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

(9)(3)(2)(9)(8)(7)