关于(类初始化)过程中(静态属性)和(非静态属性)的(执行顺序)问题的探究
写这篇博客的原因是这样的
我之前写了一篇博客 一道面试题搞懂JVM类加载机制(类被初始化的几种情况,类文件加载的过程) 然后有小伙伴在下面评论说他的执行结果跟我的不一样,所以我写这篇博客解释下原因:
首先我的代码是这样的:
class Singleton{
private static Singleton singleton = new Singleton();
public static int value1;
public static int value2 = 0;
private Singleton(){
value1++;
value2++;
}
public static Singleton getInstance(){
return singleton;
}
}
class Singleton2{
public static int value1;
public static int value2 = 0;
private static Singleton2 singleton2 = new Singleton2();
private Singleton2(){
value1++;
value2++;
}
public static Singleton2 getInstance2(){
return singleton2;
}
}
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("Singleton1 value1:" + singleton.value1);
System.out.println("Singleton1 value2:" + singleton.value2);
Singleton2 singleton2 = Singleton2.getInstance2();
System.out.println("Singleton2 value1:" + singleton2.value1);
System.out.println("Singleton2 value2:" + singleton2.value2);
}
运行的结果是这样的:

然后又小伙伴的说他的运行结果是这样的

首先我猜,可能是因为的main方法中有一处写错了,用了实例对象去点静态属性了【singleton.value1】导致编译错误,所以这位朋友拷贝的代码没法执行所以修改了部分代码,没猜错的话他应该是这么修改的 他应该是把Singleton类里的value1和value2的static属性去掉了,所以才会出现上面的结果,那我们看一下修改后的代码
public class Test { static class Singleton { private static Singleton singleton = new Singleton(); public int value1; public int value2 = 0; private Singleton(){ value1++; value2++; } public static Singleton getInstance(){ return singleton; } } static class Singleton2 { public int value1; public int value2 = 0; private static Singleton2 singleton2 = new Singleton2(); private Singleton2(){ value1++; value2++; } public static Singleton2 getInstance2(){ return singleton2; } } public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println("Singleton1 value1:" + singleton.value1); System.out.println("Singleton1 value2:" + singleton.value1); Singleton2 singleton2 = Singleton2.getInstance2(); System.out.println("Singleton2 value1:" + singleton2.value1); System.out.println("Singleton2 value2:" + singleton2.value2); } }
此执行结果就是 1,1,1,1
这个时候你是不是就要怀疑了,不是说静态属性的加载 是优先于 非静态属性的么?为什么这里却不符合这个规则,先别着急,我们把代码再改一下就看明白了
public class Test { static class Singleton { private static Singleton singleton = new Singleton(); public int value1; public int value2 = 0; public static int value3 = 0; static { System.out.println("value3 = " + value3 ); } private Singleton(){ value1++; value2++; value3++; System.out.println("Construct value3 = " + value3 ); } public static Singleton getInstance(){ return singleton; } } static class Singleton2 { public int value1; public int value2 = 0; private static Singleton2 singleton2 = new Singleton2(); private Singleton2(){ value1++; value2++; } public static Singleton2 getInstance2(){ return singleton2; } } public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println("Singleton1 value1:" + singleton.value1); System.out.println("Singleton1 value2:" + singleton.value1); System.out.println("Singleton1 value3:" + Singleton.value3); Singleton2 singleton2 = Singleton2.getInstance2(); System.out.println("Singleton2 value1:" + singleton2.value1); System.out.println("Singleton2 value2:" + singleton2.value2); } }
输出结果如下:

不是说好的静态代码块先于构造方法执行么?怎么先执行了构造方法呢?
我来解释下:
public class Test { static class Singleton { private static Singleton singleton = new Singleton(); // 2,4,11 静态属性分配内存赋默认值null , 给静态属性初始化,此时要执行Singleton构造方法,
而执行构造方法前需要必须全部初始化其非静态属性,因此执行 第五步, 赋值给singleton public int value1; // 5 赋值 0 【执行完10后处于未分配内存阶段】 public int value2 = 0; // 6 赋值 0 【执行完10后处于未分配内存阶段】 public static int value3 = 0; // 3,12 静态属性赋默认值 0 , 赋值 0 static { System.out.println("value3 = " + value3 ); //13 打印输出 } private Singleton(){ value1++; // 7 value1 = 1 value2++; //8 value2 = 1 value3++; //9 value3 = 1 System.out.println("Construct value3 = " + value3 ); // 10 打印输出 【执行完构造方法后 value1 和 value2 会被立即回收,此时处于未分配内存阶段】 } public static Singleton getInstance(){ return singleton; // 14 返回方法调用到main中 } } static class Singleton2 { public int value1; public int value2 = 0; private static Singleton2 singleton2 = new Singleton2(); private Singleton2(){ value1++; value2++; } public static Singleton2 getInstance2(){ return singleton2; } } public static void main(String[] args) { Singleton singleton = Singleton.getInstance();//1 执行静态方法 System.out.println("Singleton1 value1:" + singleton.value1); System.out.println("Singleton1 value2:" + singleton.value1); System.out.println("Singleton1 value3:" + Singleton.value3); Singleton2 singleton2 = Singleton2.getInstance2(); System.out.println("Singleton2 value1:" + singleton2.value1); System.out.println("Singleton2 value2:" + singleton2.value2); } }
首先 main 中调用 Singleton.getInstance() 静态方法,触发了 Singleton 类的初始化,首先给静态属性分配内存 > 给静态属性赋默认值 > 按顺序给静态属性初始化赋值 > 由于singleton的初始化用到了构造方法,而构造方法执行前需要先初始化其非静态属性(这一步是不是符合之前的知识点的:静态属性和非静态属性一定是先于构造方法执行的) > 执行构造方法,并且把结果赋值给静态变量singleton (构造方法执行完回收栈内存,此时value1和value2又回到主线程中的未分配内存状态,而singleton中的属性value1和value2是有值的都是1) > 给静态属性value3赋值0 > 执行静态代码块输出value3的值 0 > 最后返回getInstance方法的返回值回到main中,所以main中输出的值是内部静态变量singleton的两属性值 都是1,所以结果就是那位网友的1,1,1,1
其实你把上面的代码debug一下就清楚执行过程了
深度解读:
静态变量属于类,只执行一次,非静态变量属于对象,每次new都会执行,所以执行 private static Singleton singleton = new Singleton(); 这段代码时已经不是第一次初始化对象了,所以只会执行非静态属性,我们知道不管是静态属性还是非静态属性的初始化都是在构造方法之前执行的,所以当我们new Singleton();的时候,需要先初始化value1和value2(注意此时是在新开辟的栈中分配的新变量),而且这两个值是静态变量singleton对象的属性,而不是Singleton主类中的value1和value2,此时主类中的静态方法还未执行完,所以主类中的value1和value2肯定是未分配内存阶段
所以,类的静态属性一定优先于非静态属性赋值这句话是不准确的,从上面的代码可以看出,主类的静态代码还没执行完,非静态的属性已经被初始化一次了,这就是事实,不信的话如果你把singleton设置未非静态的,由于非静态的属性每次new都会执行,就会陷入一个死循环,最终导致抛出栈溢出的异常 java.lang.StackOverflowError 感兴趣的可以试一下
所以人们常说的静态属性 优先于 非静态属性赋值是很险隘的,在同一个对象初始化的时候可以这么理解,但是在宏观的绝对先后顺序上来说是不成立的。


浙公网安备 33010602011771号