我们知道java中类加载的过程分为:加载、连接、初始化,其中连接阶段又分为验证、准备和解析,准备阶段的动作就是为类的静态变量分配内存,并将其设置为一个默认值,java中各种数据类型的默认值如下:
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
char | (空) |
boolean | false |
引用类型 | null |
初始化过程就是为类的静态变量赋予正确的初始值。
很多人可能认为静态变量在类中的位置是平行的,位置不同不会导致变量的值不同,这里举两个例子来深刻理解一下准备阶段以及初始化阶段的成员变量的值是如何变化的:
例1:
public class MyTest {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2: " + Singleton.counter2);
}
}
class 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;
}
}
输出结果为:
counter1: 1
counter2: 1
在main方法中调用了Singleton 类的静态方法getInstance,因此属于对该类的主动使用,因此Singleton 会进行初始化,关于对类的主动使用在我的另外一篇博客“类加载器的阶段分析”https://blog.csdn.net/weixin_42105936/article/details/100171009中有详细描述。
在初始化阶段发生之前,先会进行准备阶段,也是就为变量赋予默认值,准备阶段完成之后,各个变量的初始值分别为:
counter1:0
counter2:0
singleton:null
接下来就是初始化:为类的静态变量赋予正确的初始值,初始化顺序如下:
- 由于counter1,没有额外指定初始值,所以维持默认值0;
- counter2被赋予新的值,仍然为0;
- 在给singleton 初始化时,由于使用了new关键字,所以需要执行构造方法,执行counter1++,
counter2++后counter1和counter2都自增变成了1;
在初始化阶段完毕之后,counter1为1,counter2为1,因此控制台输出结果如上。
例2:
public class MyTest {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2: " + Singleton.counter2);
}
}
class Singleton {
public static int counter1;
private static Singleton singleton = new Singleton();
private Singleton() {
counter1++;
counter2++;
}
public static int counter2 = 0;
public static Singleton getInstance() {
return singleton;
}
}
输出结果为:
counter1: 1
counter2: 0
例2代码与例1代码的唯一不同,就是成员变量counter2 和singleton的位置不同,于是就会产生疑问:静态成员的位置不同也会导致变量成员值的不同吗?
我们还是来分析一下:
首先还是准备阶段,给变量依次赋予默认值之后:
counter1:0
singleton:null
counter2:0
同样类需要初始化,初始化步骤如下:
由于counter1,没有额外指定初始值,所以维持默认值0;
在给singleton 初始化时,由于使用了new关键字,所以需要执行构造方法,执行counter1++,
counter2++,在此之前,counter1和counter2都是0,自增之后,counter1和counter2都变成了1;counter2初始化,被赋予新的值,变成0;
由上面的两个例子加分析后我们得出一个结论:静态变量在初始化时是顺序执行初始化工作的,静态变量的位置不同有可能导致最终的值不同。关键还是要理解准备阶段和初始化阶段JVM的所进行的动作。