【2021-06-30】分析程序的运行结果,并解释为什么?

292 阅读2分钟

请移步至 【DobbyKim 的每日一题】 查看更多的题目~

第一题

MyTestClass

public class MyTestClass {
    private static MyTestClass myTestClass = new MyTestClass();

    private static int a = 0;
    private static int b;

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

    public static MyTestClass getInstance() {
        return myTestClass;
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }
}

Test

public class Test {

    public static void main(String[] args) {
        MyTestClass myTestClass = MyTestClass.getInstance();
        System.out.println("myTestClass.a : " + myTestClass.getA());
        System.out.println("myTestClass.b : " + myTestClass.getB());

    }
}

请说出程序的执行结果,并解释为什么?

第二题

MyTestClass2

public class MyTestClass2 {

    private static int a = 0;
    private static int b;

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

    private static final MyTestClass2 myTestClass2 = new MyTestClass2();

    public static MyTestClass2 getInstance(){
        return myTestClass2;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        MyTestClass2 myTestClass2 = MyTestClass2.getInstance();
        System.out.println("myTestClass2.a : " + myTestClass2.getA());
        System.out.println("myTestClass2.b : " + myTestClass2.getB());
    }
}

请说出该程序的执行结果,并解释为什么?

答:

第一题的答案为:

myTestClass.a : 0
myTestClass.b : 1

本题考查的知识点为类加载的顺序,类从被加载至 JVM 到卸载出内存整个生命周期为:

【2021-6-30】分析程序的运行结果,并解释为什么?

各个阶段的主要功能:

  • 加载:查找并加载类文件的二进制数据

  • 连接:将已经读入内存的类的二进制数据合并到 JVM 运行时环境中去,包含如下几个步骤:

    • 验证:确保被加载类的正确性

    • 准备:为类的静态变量分配内存,赋默认值;例如:public static int a = 1; 在准备阶段对静态变量 a 赋默认值 0

    • 解析:把常量池中的符号引用转换成直接引用

  • 初始化:为类的静态变量赋初始值,这个时候才对静态变量 a 赋初始值 1

我们可以看到,Java 中,类的静态成员在类加载时就已经被加载到内存中了!

就本题,我们来分析下类加载的过程:

首先在连接的准备阶段JVM 会为类的静态变量分配内存,并赋缺省值,即:

myTestClass = null;
a = 0;
b = 0;

接着,在类的初始化阶段,会为这些静态变量赋初始值

这句话会回调构造器

a++;b++;导致 a 和 b 的结果均为 1

然后代码执行到:

a = 0;
b;

这个时候,执行对 a 和 b 真正的初始化赋值

又将 a 变为了 0 ;而 b 则没有赋值结果仍然是 1;所以输出结果为

myTestClass.a : 0
myTestClass.b : 1

第二题的结果为:

myTestClass2.a : 1
myTestClass2.b : 1

我们再次按照类的初始化顺序进行分析:

首先在连接的准备阶段JVM 会为类的静态变量分配内存,并赋缺省值,即:

a = 0;
b = 0;
myTestClass2 = null;

然后,在类的初始化阶段,会为这些静态变量赋初始值

首先,代码执行到:

a = 0;
b;

a 赋初始值为 0,然后 b 没有赋值,其结果还是 0

接着,执行到语句:

myTestClass = new MyTestClass();

这句话会回调构造器

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

执行 a++;b++;,导致 a 和 b 的结果均为 1,所以最终程序输出的结果为:

myTestClass2.a : 1
myTestClass2.b : 1