Java类的初始化顺序

2021-02-14  本文已影响0人  程序员汪汪

本文主要演示Java类的初始化顺序,分为有继承和没有继承两种情况。如有错误,劳烦指正,不胜感谢!

没有继承情况

直接上代码,工具类:

/**
 * 工具类Log
 */
public class Log {

    public static String baseFieldInit() {
        System.out.println("父类普通成员变量");
        return "";
    }

    public static String baseStaticFieldInit() {
        System.out.println("父类静态成员变量");
        return "";
    }

    public static String fieldInit() {
        System.out.println("子类普通成员变量");
        return "";
    }

    public static String staticFieldInit() {
        System.out.println("子类静态成员变量");
        return "";
    }
}

父类:

/**
 * 父类/基类 为了方便后面有继承的情况,所以这里直接使用父类
 */
public class Base {

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

    private static String staticValue = Log.baseStaticFieldInit();

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

    {
        System.out.println("父类构造代码块 1");
    }

    private String value = Log.baseFieldInit();

    {
        System.out.println("父类构造代码块 2");
    }

    Base() {
        System.out.println("父类构造方法");
    }

    public static void main(String[] args) {
        //在 new 对象之前打印输出只是为了证明main方法虽然是程序入口,但是并不会最先执行
        System.out.println("new 之前");
        new Base();
        System.out.println("new 之后");
    }

}

输出:

父类静态代码块 1
父类静态成员变量
父类静态代码块 2
new 之前
父类构造代码块 1
父类普通成员变量
父类构造代码块 2
父类构造方法
new 之后

结论:

  1. 静态代码块和静态成员变量并列优先级,按代码中出现的先后顺序执行,只有在第一次加载类时执行。
  2. 在执行完静态代码块之后,会执行main方法中,实例化对象(new)之前的代码。
  3. 构造代码块和普通成员变量并列优先级,按代码中出现的先后顺序执行。
  4. 构造方法。
  5. 最后就是实例化对象(new)之后的代码。

注意:第1步只有在类第一次被加载的时候才运行。如果创建两个对象

new Base();
new Base();

第二次创建对象,就只执行第3步。

有继承的情况

这里创建一个子类Subclass继承上述中的父类Base,同时也使用了工具类Log代码如下:

/**
 * 子类/派生类
 */
public class SubClass extends Base {

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

    private static String staticValue = Log.staticFieldInit();

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

    {
        System.out.println("子类普通代码块 1");
    }

    private String value = Log.fieldInit();

    {
        System.out.println("子类普通代码块 2");
    }

    SubClass() {
        System.out.println("子类构造方法");
    }

    public static void main(String[] args) {
        System.out.println("new 之前");

        SubClass sub = new SubClass();
        System.out.println("new 之后");
    }
}

输出:

父类静态代码块 1
父类静态成员变量
父类静态代码块 2
子类静态代码块 1
子类静态成员变量
子类静态代码块 2
new 之前
父类构造代码块 1
父类普通成员变量
父类构造代码块 2
父类构造方法
子类普通代码块 1
子类普通成员变量
子类普通代码块 2
子类构造方法
new 之后

结论:

  1. 父类静态代码块和静态成员变量并列优先级,按代码出现先后顺序执行。
  2. 子类静态代码块和静态成员变量并列优先级,按代码出现先后顺序执行。
  3. main方法中实例化对象(new)之前的代码。
  4. 父类构造代码块和普通成员变量并列优先级,按代码出现先后顺序执行。
  5. 父类构造方法。
  6. 子类构造代码块和普通成员变量并列优先级,按代码出现先后顺序执行。
  7. 子类构造方法。
  8. main方法中实例化对象(new)之前的代码。

注意,第1,2步只有在子类第一次被加载时才运行,如果创建两个对象,则第二次创建对象只执行第4,5,6,7步。

总结

在没有继承的情况下,优先级:静态代码块和静态成员变量 > 构造代码块和普通成员变量 > 构造方法。

有继承的情况下,优先级:父类静态代码块和静态成员变量 > 子类静态代码块和静态成员变量 > 父类构造代码块和普通成员变量 > 父类构造方法 > 子类构造代码块和普通成员变量 > 子类构造方法。

main方法是一个静态方法,在通过java命令运行SubClass时,产生的第一个事件就是试图访问SubClass.main(),于是类加载器开始启动并找到SubClass.class文件,对它进行加载时,发现这是一个子类(有extends关键字),于是继续加载它的父类Base,这个过程,不管是否创建了父类对象都会进行。如果父类还有父类,那么还会继续加载,依次类推。在静态代码块和静态成员变量都加载完成后,就会执行main方法中的其他代码,如果遇到了实例化对象的代码,就会按照上述所说的优先级执行代码。

如果对你有帮助就请点个赞吧。

上一篇 下一篇

猜你喜欢

热点阅读