字节码加载和class实例的顺序问题

刷头条的时候看到了这个:  你做会错的一道Java面试题:字节码加载和class实例的顺序问题

以前也看到过,应该是阿里的校招笔试题,当时懒得理这种工作中毫无意义的东西。

今天突然来了兴趣,就想看看能不能靠自己的知识来理清其中的逻辑,结果还不错,(*^_^*)。

原题略绕,不直观,先来个精简版:

 1 public class A {
 2     static{
 3         System.out.println("我是静态代码块");
 4     }
 5 
 6     {
 7         System.out.println("实例代码块");
 8     }
 9 
10     public A(){
11         System.out.println("构造方法");
12     }
13 
14     public static void main(String[] args){
15         new A(); //放开注释,执行,看一下实例代码块和构造方法的执行顺序
16     }
17 }

上面的代码,直接执行,即可观察到创建实例时的加载执行顺序

执行结果如下:

我是静态代码块
实例代码块
构造方法

很明显,一般情况下,创建实例的执行顺序就是:静态代码块 > 实例代码块 > 构造方法。

不知道你们有没有考虑过,为什么要按照这种顺序来?

其实很简单,要满足依赖前置条件!  

第一点:因为静态变量的可以被直接调用,所以静态变量部分必须先加载并创建;

第二点:因为静态代码块是在所有静态变量加载完毕后一次性执行的,所以其执行要晚于所有静态变量的加载; 

第三点:实例代码块是在创建实例的时候执行的,确切的说,是在实例的内存空间分配完毕,而在构造进行之前执行的。-- 如果学过C++,你应该知道这个。 

OK,已经有了上面三点,那我们再来推一下该面试题中的情况。

还是先用简化版本,方便看懂:

public class A {
    private static A a = new A(); //静态实例变量

    static{
        System.out.println("我是静态代码块");
    }

    {
        System.out.println("实例代码块");
    }

    public A(){
        System.out.println("构造方法");
    }

    //TODO 执行空的main方法,就是只加载字节码。可以有效测试加载过程中的顺序。
    public static void main(String[] args){
        // new A(); //放开注释,执行,看一下实例代码块和构造方法的执行顺序
    }
}

相对于前面那一版,这里只增加了一个本类的静态实例变量 a 。当然还注释掉了main方法中的 new A(),因为我们不需要额外的实例创建来干扰观察。

执行main方法,结果如下:

实例代码块
构造方法
我是静态代码块

用上面三条依赖前置条件推理一下看看:

①先创建静态实例变量,结果执行new A();②而创建实例必然导致实例代码块的先执行,再执行构造代码块!

③执行完构造代码块,静态实例变量a就构造完毕,此时所有静态变量构建完毕,于是执行静态代码块!

原文中有多个静态变量、静态实例变量,静态实例变量的创建还会修改静态变量,让推理每一步静态变量的值。

其实再加上一条即可推理出来:按照声明顺序加载

总之,虽然知道了原理,但是很烦这种推理题 - 把人搞得晕晕的。

原文地址:https://www.cnblogs.com/larryzeal/p/8135628.html