这是一个经典例子,很多面试题中出现过。首先引入正常的情况:
1package com.test.jvm.learn01;
2
3public class Main2 {
4 public static void main(String[] args) {
5 System.out.println(Counter.counter1);
6 System.out.println(Counter.counter2);
7 }
8}
9
10
11package com.test.jvm.learn01;
12
13public class Counter {
14
15 public static int counter1;
16 public static int counter2 = 0;
17 public static Counter counter = new Counter();
18
19 private Counter() {
20 counter1++;
21 counter2++;
22 System.out.println(counter1);
23 System.out.println(counter2);
24 }
25
26 public static Counter getInstance() {
27 return counter;
28 }
29}
输出结果为:
11
21
31
41
如果把第6行private static int counter2 = 0;挪到第15行,即:
1package com.test.jvm.learn01;
2
3public class Counter {
4
5 public static int counter1;
6 public static Counter counter = new Counter();
7
8 private Counter() {
9 counter1++;
10 counter2++;
11 System.out.println(counter1);
12 System.out.println(counter2);
13 }
14
15 public static int counter2 = 0;
16
17 public static Counter getInstance() {
18 return counter;
19 }
20}
然后输出的结果:
11
21
31
40
第一种情况是正常输出,下面从类的加载和初始化上解释第二种情况的结果:
- main方法中调用了Counter类的静态字段Counter.counter1,所以Counter类被主动使用,触发Counter类的初始化。
- 在类的连接过程中的准备阶段,静态变量分配内存且赋上默认值,所以从上到下,counter1=0,counter =null,counter2 =0
- 然后在类的初始化过程中,从上到下依次初始化各个字段:counter1为初始化为0
- 在初始化counter时需要创建Counter对象,Counter的构造方法被调用,所以初始化counter后,counter1=1,counter2=1
- 初始化counter引用后,紧接着初始化counter2,此时counter2被显式赋值为0,所以此时,counter1=1,counter2=0
- 最后打印输出即为第4步的结果