http://bookshadow.com/weblog/2016/02/28/java-variable-initialization-problem/
首先来看笔者遇到的一道面试题,阅读下面的代码并给出执行结果:
class Singleton {
private static Singleton singleton = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return singleton;
}
}
public class TestSingleton {
public static void main(String[] args) {
Singleton s = Singleton.getInstance();
System.out.println(Singleton.counter1);
System.out.println(Singleton.counter2);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
读完这段代码,笔者的第一个念头是由于counter1没有赋初始值,所以会导致编译错(害羞),但面试官指出这段代码可以通过编译。
然后笔者又给出了答案“1 1”,因为笔者觉得 public static int counter2; 与 public static int counter2 = 0; 是等价的。
但实际上程序的实际运行结果是“1 0”,这是为什么呢?
Java中的变量根据作用域可划分为成员变量和局部变量两类。
1. 对于局部变量,未经初始化直接使用会导致编译错误(The local variable may not have been initialized),因此在使用局部变量之前必须对其显式地初始化。
2. 对于成员变量,Java会在声明时将其赋值为缺省值(基本数据类型为0,对象类型为null)。
而需要注意的是,Java对于成员变量的初始化,实际上是分解为两步执行的。
对于静态成员变量:
1. 根据静态成员变量在代码中的先后次序进行声明并赋值为缺省值
2. 在静态块内依次为变量进行赋值
对于动态成员变量:
1. 根据动态成员变量在代码中的先后次序进行声明并赋值为缺省值
2. 在动态块内依次为变量进行赋值
上面的Singleton类的初始化过程实际上可以转化为:
class Singleton {
private static Singleton singleton;
public static int counter1;
public static int counter2;
static {
singleton = new Singleton();
counter2 = 0;
}
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return singleton;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
执行过程如下:
首先将singleton, counter1, counter2分别赋值为null, 0, 0
然后执行静态块中的语句:
对创建的Singleton对象赋值给singleton变量,执行Singleton的构造方法,对counter1和counter2的值分别+1
然后将counter2赋值为0
因此最终结果是“1 0”
如果将Singleton的counter1和counter2更改为动态变量,结果又是什么呢?
class Singleton {
private static Singleton singleton = new Singleton();
public int counter1;
public int counter2 = 0;
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return singleton;
}
}
public class TestSingleton {
public static void main(String[] args) {
Singleton s = Singleton.getInstance();
System.out.println(s.counter1);
System.out.println(s.counter2);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
Singleton类的代码可以转化为:
class Singleton {
private static Singleton singleton;
public int counter1;
public int counter2;
static {
singleton = new Singleton();
}
{
counter2 = 0;
}
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return singleton;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
此时,Singleton类初始化的顺序如下:
首先将singleton, counter1, counter2分别赋值为null, 0, 0
然后执行静态块中的语句:
对创建的Singleton对象赋值给singleton变量,首先执行动态块中的语句,将counter2赋值为0
然后执行Singleton的构造方法,对counter1和counter2的值分别+1
运行结果是“1 1”