昨晚写 深入 java 虚拟机学习 -- 类的加载机制 都到 1 点半了, 由于第二天还要工作, 没有将上篇文章中的 demo 讲解写出来, 今天抽时间补上昨晚的例子讲解
这里我先把昨天的两份代码贴过来, 重新看下:
- class Singleton
- {private static Singleton singleton = new Singleton(); // 第一份代码的位置
- public static int counter1;
- public static int counter2=0;
- private static Singleton singleton = new Singleton();// 第二份代码的位置
- private Singleton(){
- counter1++;
- counter2++;
- }
- public static Singleton getInstance(){
- return singleton;
- }
- }
- public class Demo
- {
- public static void main(String[] args){
- Singleton singleton=Singleton.getInstance();
- System.out.println("counter1:"+singleton.counter1);
- System.out.println("counter2:"+singleton.counter2);
- }
- }
第一份代码执行结果:
第二份代码执行结果:
类是如何被加载的
让我们再来回顾下上篇文章的加载顺序
我们知道 Java 虚拟机为类的静态变量分配内存, 并设置默认的初始值实在准备阶段开始的, 这里所设置的初始值通常情况下是类型默认的零值 (如 00Lnullfalse 等), 而不是被在 Java 代码中被显式地赋予的值有很多人还是不太明白默认零值和显示赋值到底是什么意思, 下面我们距离来说:
- public class Sample{
- private static int a=1;
- private static int b;
- }
上面的代码在经过了准备阶段后的结果是:
a=0; b=0;
大家可能对 b=0 没有任何疑问, 而 a=0; 就是上面说的类型默认的零值, 也就是说准备的阶段等号右边的 1 并不会赋值给 a, 不知道这么解释大家能不能明白, 而类初始化阶段是类加载过程的最后一步, 到了初始化阶段, 才真正开始执行类中定义的 java 程序代码在初始化阶段, Java 虚拟机执行类的初始化语句, 为类的静态变量 赋予正确的初始值
private static int a=1; 表示 a 被显式初始化成 1;
private static int b; 这里的 b 并没有被显式初始化, 所以此时 b 的值仍然为 0;
案例分析
好了, 说了这么多开始分析案例, 我们知道类是自上而下执行的, 所以第一份代码解析如下
当 Singletom 类在准备阶段, 由于只是分配数据类型默认值, 所以此时的 counter1=0counter2=0;
当 Singletom 类在初始化阶段, 1 调用了 Singleton 的实例并对 counter1 和 counter2 分别进行了 ++ 操作, 所以此时的 counter1=1,counter2=1, 由于 2 中未对 counter1 进行显式初始化, 所以此时的 counter1 仍然保留值 1, 而 counter2 被显式赋值成 0, 所以 counter2 在初始化阶段又被改为 0
在经过了准备初始化阶段后的最终结果就变成了 counter1:1counter2:0
第二份代码是将 1->2->3 的顺序修改为 2->3->1, 我们按着上面的思路重新分析发现很清晰的就知道了结果
准备阶段没有任何变化, counter1=0counter2=0;
初始化阶段, counter1 没有被显式赋值, 所以 counter1 仍然保留值 0,counter2 被显式赋值为 0, 所以 counter2=0, 到第三步时调用了 Singleton() 方法, 此时执行了 ++ 操作
最终结果 counter1:1counter2:1
来源: https://www.cnblogs.com/blueskyli/p/8487774.html