Java类初始化顺序

只恨上课的时候没有认真听讲,在网易笔试的过程中碰到了Java类初始化顺序的题目。
马上重新学习,不敢怠慢,写下此文


无继承的类的初始化过程

在Java中类的初始化优先级是: 静态 > 实例 > 构造器

静态:包括静态变量,静态初始化块,也就是关键字 static 修饰部分的代码,静态部分的初始化,按照类内的定义顺序执行初始化。
实例:和C++不同,Java的实例变量(instance variable)支持类内初始化,这里的“实例”,指的就是实例变量、或实例初始化块。执行顺序也是按照类内定义的顺序进行初始化
构造器:就是构造方法,这个就不用多说

看下面的实验代码

/**
 * @author Lisupy
 * @email zhong550413470@gmail.com
 * 类初始化测试
 * */
public class InitialOrderTest {

    // instance variable
    private String val = "instance val";

    // instance initial block
    {
        System.out.println(val);
        System.out.println("Executing in instance initial block");
    }

    // static variable
    private static String staticVal = "static val";

    // static initial block
    static {
        System.out.println(staticVal);
        System.out.println("Executing in static block");
    }

    // Constructor
    public InitialOrderTest(){
        System.out.println("Executing in Constructor");
    }

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

这段代码运行的结果为

static val
Executing in static block
instance val
Executing in instance initial block
Executing in Constructor
instance val
Executing in instance initial block
Executing in Constructor

静态初始化部分在类被加载的时候先执行且执行一次,实例初始化部分在每次创建新的实例对象时先运行,再运行构造方法
上面的代码中,由于静态初始化代码的静态块依赖于静态变量,所以二者的顺序不能替换,替换后会出现编译错误 java: illegal forward reference, 这也证明了代码的执行顺序是按照类内定义的顺序执行的。


有继承关系的类的初始化过程

对于有继承关系的类的初始化情况,比上面的复杂,所以也经常列为考点。但是再复杂也万变不离其宗, 就是优先级按照 "静态">"实例">"构造器", 但是有稍微一点不同,看下面的代码

/**
 * 代码转于http://www.cnblogs.com/jackyrong/archive/2008/08/12/1266161.html
 */
class Parent {   
    // 静态变量   
    public static String p_StaticField = "父类--静态变量";   
    // 变量   
    public String p_Field = "父类--变量";   
  
    // 静态初始化块   
    static {   
        System.out.println(p_StaticField);   
        System.out.println("父类--静态初始化块");   
    }   
  
    // 初始化块   
    {   
        System.out.println(p_Field);   
        System.out.println("父类--初始化块");   
    }   
  
    // 构造器   
    public Parent() {   
        System.out.println("父类--构造器");   
    }   
}   
  
public class SubClass extends Parent {   
    // 静态变量   
    public static String s_StaticField = "子类--静态变量";   
    // 变量   
    public String s_Field = "子类--变量";   
    // 静态初始化块   
    static {   
        System.out.println(s_StaticField);   
        System.out.println("子类--静态初始化块");   
    }   
    // 初始化块   
    {   
        System.out.println(s_Field);   
        System.out.println("子类--初始化块");   
    }   
  
    // 构造器   
    public SubClass() {   
        System.out.println("子类--构造器");   
    }   
  
    // 程序入口   
    public static void main(String[] args) {   
        new SubClass();   
    }   
}  

运行结果如下

父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器

对于这个运行结果,说一说我的见解:
总体来说,初始化过程满足上面所说的优先级 "静态">"实例">"构造器",但是要添加一个另外的条件"父类">"子类"
就上述的代码来说,在类加载的过程中就必须满足 "父类" > "子类",所以父类的静态代码 优先于 子类的静态代码,符合上述运行结果
实例初始化代码是在创建类的过程中运行的,由于子类的初始化可能会依赖到父类的初始化的成员,所以先运行父类的实例初始化块和构造方法,再运行子类的初始化块和构造方法


上述的内容均属于我自己的理解,有可能和实际有所区别,如果各位看官发现任何错误,请指出来,我会听取您的意见改善这篇博文。

原文地址:https://www.cnblogs.com/pluviophile/p/7513790.html