---
title: 创建一个自身类的静态对象变量，究竟会如何执行？
author: 查尔斯
date: 2022/03/24 21:30
categories:
 - Bug万象集
tags:
 - Java
 - JVM
---

# 创建一个自身类的静态对象变量，究竟会如何执行？

## 前言

**C：** 近两周在疯狂给项目组面试招聘，昨天晚上10点多，产品总监在面试群里发了一道题，问运行结果是什么，题目如下：

```java {2-4}
class Singleton {
    private static Singleton singleton = new Singleton();
    public static int count1;
    public static int count2 = 3;

    private Singleton() {
        count1++;
        count2++;
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

public class Test {
    public static void main(String[] args) {
        Singleton singleTon = Singleton.getInstance();
        System.out.println("count1=" + singleTon.count1);
        System.out.println("count2=" + singleTon.count2);
    }
}
```

这激起了我们几个干技术的热情，那就分析一下吧。

## 简单分析

1、简单看了下题目，这不是一个采用了饿汉式单例模式的单例类嘛，接下来当然是去找程序入口了。

2、在 Test 类的 main 方法中，首先调用了 Singleton 类的 getInstance() 方法，很显然这是要获取 Singleton 这个单例类的唯一对象（实例）了。

3、然后在获取到唯一对象（实例）之后，输出了 Singleton 类的两个静态成员变量 count1、count2 的值。（虽然通过对象名调用静态信息这种方式不推荐，但是对结果没有影响）

4、看到这儿，两个类里也没别的地方有输出语句，所以最终运行结果就是要看看 count1、count2 的输出值了。

5、**重点来了：** 在调用 getInstance() 方法前，由于 Singleton 类没有加载，所以肯定要先加载类，由于 count1、count2、Singleton 的唯一对象（实例）都是静态的，所以它们会随着类的加载而加载。其中 int 类型的 count1 变量没有指定初始值，那默认值就是 0，count2 指定了初始值是 3， Singleton 类的唯一对象（实例）要创建会调用构造方法，构造方法里又对 count1 和 count2 进行了自增 1 的运算，那结果自然就是 count1 是 1，count2 是 4。

这么一顿火花带闪电的分析后，自信的将答案发到了群里。

```
count1=1
count2=4
```

## 深度分析

很显然答错了，不然也不会单独记录了。之所以答错了，是因为忽略了静态信息的加载顺序，静态信息的加载顺序是由编码顺序决定的，上方分析中先入为主的把 count1 和 count2 加载完了，但实际上最先执行的是 Singleton 的唯一对象（实例）创建及变量赋值，随后才是执行 count1、count2。

我们可以通过 `javap -c Singleton.class` 反汇编一下字节码文件，反汇编后的 JVM 指令如下：

```java
Compiled from "Test.java"
class org.example.Singleton {
  public static int count1;

  public static int count2;

  public static org.example.Singleton getInstance();
    Code:
       // 获取 singleton 静态对象变量，并将其值压入栈顶
       0: getstatic     #4                  // Field singleton:Lorg/example/Singleton;
       // 从当前方法返回 singleton 对象引用
       3: areturn

  static {};
    Code:
       // 1、创建 Singleton 类的对象，并赋值给静态对象变量 singleton
       // 1.1 创建对象
       0: new           #5                  // class org/example/Singleton
       // 1.2 复制栈顶数值并将复制值压入栈顶
       3: dup                        
       // 1.3 调用 Singleton 类构造方法，count1 和 count2 自增 1，此时 count1 为 1，count2 为 1
       4: invokespecial #6                  // Method "<init>":()V
       // 1.4 对象创建成功将对象引用赋值给静态对象变量 singleton
       7: putstatic     #4                  // Field singleTon:Lorg/example/Singleton;
      
      // 2、将 3 赋值给 count2
      // 2.1 将 int 型 3 推送至栈顶
      10: iconst_3                         
      // 2.2 为 count2 静态变量赋值
      11: putstatic     #3                  // Field count2:I
      
      // 3、结束方法
      14: return                            
}

```

很显然了，count2 最后是被赋值为 3 了。

正确答案就是：

```
count1=1
count2=3
```

## 额外扩展

那如果真的想得到之前的结果呢？

```
count1=1
count2=4
```

只需要将 count1、count2 两个静态变量的顺序调整到 Singleton 类的唯一对象（实例）变量上方就可以了。

```java {2-4}
class Singleton {
    public static int count1;
    public static int count2 = 3;
    private static Singleton singleton = new Singleton();

    private Singleton() {
        count1++;
        count2++;
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

public class Test {
    public static void main(String[] args) {
        Singleton singleTon = Singleton.getInstance();
        System.out.println("count1=" + singleTon.count1);
        System.out.println("count2=" + singleTon.count2);
    }
}
```

我们再次通过 `javap -c Singleton.class` 反汇编一下字节码文件，反汇编后的 JVM 指令如下：

```java
Compiled from "Test.java"
class org.example.Singleton {
  public static int count1;

  public static int count2;

  public static org.example.Singleton getInstance();
    Code:
       // 获取 singleton 静态对象变量，并将其值压入栈顶
       0: getstatic     #4                  // Field singleton:Lorg/example/Singleton;
       // 从当前方法返回 singleton 对象引用
       3: areturn

  static {};
    Code:
       // 1、将 3 赋值给 count2，count2 此时为 3
       // 1.1 将 int 型 3 推送至栈顶
       0: iconst_3
       // 1.2 为 count2 静态变量赋值
       1: putstatic     #3                  // Field count2:I
       
       // 2、创建 Singleton 类的对象，并赋值给静态对象变量 singleton
       // 2.1 创建对象
       4: new           #5                  // class org/example/Singleton
       // 2.2 复制栈顶数值并将复制值压入栈顶
       7: dup
       // 2.3 调用 Singleton 类构造方法，count1 和 count2 自增 1，count1 此时为 1，count2 此时为 4
       8: invokespecial #6                  // Method "<init>":()V
      // 2.4 对象创建成功将对象引用赋值给静态对象变量 singleton
      11: putstatic     #4                  // Field singleTon:Lorg/example/Singleton;

      // 3、结束方法
      14: return
}

```

很显然了，count2 最后是被自增为 4 了。