JVMJVM · Java虚拟机原理 · JVM上语言·框架· 生态系统

JVM系列之类的初始化

2021-03-31  本文已影响0人  简楼

类的初始化

前言

类孕育了新的生命周期,那么接下来就是让他健康的茁壮成长,对否?

案例1

public class Client {
    // 静态变量前面
    private static Client client = new Client();

    public static int a;
    public static int b = 0;

    private Client() {
        a++;
        b++;
    }

    public static Client getInstance() {
        return client;
    }

    public static void main(String[] args) {
        Client instance = Client.getInstance();
        System.out.println("a= " + Client.a);
        System.out.println("b= " + Client.b);
    }
}

输出值:

a= 1
b= 0

案例2

public class Client {

    public static int a;
    public static int b = 0;
    // 静态变量后面
    private static Client client = new Client();

    private Client() {
        a++;
        b++;
    }

    public static Client getInstance() {
        return client;
    }

    public static void main(String[] args) {
        Client instance = Client.getInstance();
        System.out.println("a= " + Client.a);
        System.out.println("b= " + Client.b);
    }
}

输出值:

a= 1
b= 1

思考:为什么两次输出的结果不同?

理论

类的初始化,本质上是执行类构造器<client>方法,Java 虚拟机会通过加锁来确保 <clinit> 方法仅被执行一次;

<client>方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。编译器收集的顺序是有语句在资源文件中出险的顺序所决定的。

解析

眼尖的小伙伴,肯定能看出来,private static Client client = new Client(); 的位置不同,才导致结果不同。

那为什么因为顺序不同,结果就不一样了么?如果你了解类的初始化过程就知道了!

我们拿案例1解析

首先,执行main方法的Client.getInstance(),因为这个类从来没有加载过,就会触发类的加载(上一篇:类的准备与解析,解释了会发生什么);

①此时解析的结果是:

client=null
a=0
b=0

解析之后,就是初始化了
执行到private static Client client = new Client()·时:
②结果是:

client=Client@1b4fb997
a=1
b=1

继续往下执行,到public static int b = 0;执行结束时,b被再次赋值,覆盖了之前的数据,a则没有再次赋值的操作所以就没有改变;
③此时结果是:

client=Client@1b4fb997
a=1
b=0

案例2 初始化过程与案例1相同,只是client初始话的顺序变了,然后②和③就交换了位置,以至变量b最后执行的操作是b++

总结

初始化顺序:

父类静态成员和静态代码块> 子类静态成员和静态代码块 > 父类普通成员和普通代码块 > 父类构造函数>子类普通成员和普通代码块>子类构造函数

`注意`:同样等级的代码按照代码从上至下的顺序执行。

在编写或梳理代码的时候要注意代码的顺序,以防程序最后的输出结果和我们的预期不一致。

上一篇下一篇

猜你喜欢

热点阅读