博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Static变量和实例变量的初始化顺序问题
阅读量:7103 次
发布时间:2019-06-28

本文共 7285 字,大约阅读时间需要 24 分钟。

问题重现

让我们先来看一下以下的程序:

 1 
public
 
class
 StaticInitSequence {
 2
 
    
//
-------------------Static fields-------------------
 3
 
    
private
 
static
 
int
 staticIntVar 
=
 
10
;
 4
 
    
private
 
static
 
int
 staticComputeIntVar 
=
 (
int
)(Math.random() 
*
 
10
);
 5
 
    
private
 
static
 String staticStrVar 
=
 
"
Static field init(before)
"
;
 6
 
    
private
 
static
 Object staticRefVar 
=
 
new
 Object();
 7
 
   
 8
 
    
static
 {
 9
 
       staticIntVar 
=
 
20
;
10
 
       staticStrVar 
=
 
"
Static block init(before)
"
;
11
 
       staticAfterIntVar 
=
 
40
;
12
 
       staticAfterStrVar 
=
 
"
Static block init(after)
"
;
13
 
    }
14
 
   
15
 
    
private
 
static
 
int
 staticAfterIntVar 
=
 
30
;
16
 
    
private
 
static
 String staticAfterStrVar 
=
 
"
Static field init(after)
"
;
17
 
   
18
 
    
//
---------------------Instance fields----------------
19
 
    
private
 
int
 fieldIntVar 
=
 
100
;
20
 
    
private
 
int
 fieldComputeIntVar 
=
 (
int
)(Math.random() 
*
 
100
);
21
 
    
private
 String fieldStrVar 
=
 
"
Instance field init(before)
"
;
22
 
   
23
 
    
public
 StaticInitSequence() {
24
 
       fieldIntVar 
=
 
200
;
25
 
       fieldStrVar 
=
 
"
Constructor field init(before)
"
;
26
 
      
27
 
       fieldAfterIntVar 
=
 
400
;
28
 
       fieldAfterStrVar 
=
 
"
Constructor field init(after)
"
;
29
 
    }
30
 
   
31
 
    
private
 
int
 fieldAfterIntVar 
=
 
300
;
32
 
    
private
 String fieldAfterStrVar 
=
 
"
Instance field init(after)
"
;
33
 
   
34
 
    
public
 
void
 print() {
35
 
       System.out.println(
"
----------------Static Fields------------
"
);
36
 
       System.out.println(
"
staticIntVar: 
"
 
+
 staticIntVar);
37
 
       System.out.println(
"
staticComputeIntVar: 
"
 
+
 staticComputeIntVar);
38
 
       System.out.println(
"
staticStrVar: 
"
 
+
 staticStrVar);
39
 
       System.out.println(
"
staticRefVar: 
"
 
+
 staticRefVar);
40
 
       System.out.println(
"
staticAfterIntVar: 
"
 
+
 staticAfterIntVar);
41
 
       System.out.println(
"
staticAfterStrVar: 
"
 
+
 staticAfterStrVar);
42
 
      
43
 
       System.out.println(
"
-----------------Instance Fields---------
"
);
44
 
       System.out.println(
"
fieldIntVar : 
"
 
+
 fieldIntVar);
45
 
       System.out.println(
"
fieldComputeIntVar : 
"
 
+
 fieldComputeIntVar);
46
 
       System.out.println(
"
fieldStrVar : 
"
 
+
 fieldStrVar);
47
 
       System.out.println(
"
fieldAfterIntVar : 
"
 
+
 fieldAfterIntVar);
48
 
       System.out.println(
"
fieldAfterStrVar : 
"
 
+
 fieldAfterStrVar);
49
 
    }
50
 
}

如果我们调用以上类的print()方法(new StaticInitSequence().print()),会有什么样的结果呢?

我自认为,直接对一个字段初始化是编译器提供支持的一种编程方式,这种编程方式可以提高代码的可读性,因为用户可以直接知道一个字段的初始值,而不用到构造函数或者静态语句块里面去找。在Java中,实际编译后的二进制文件中,所有的字段初始化语句都放在了初始化函数中(类(静态)初始化函数(<clinit>)或者实例初始化(构造函数/<init>)函数)。因此在我的逻辑思维中,在源代码中,初始化函数应该可以改变字段初始化中的值,这样还就可以在字段初始化中提供一个初始值,而在初始化函数中根据需要改变它。然而另我感到意外的是Java中只有实例初始化机制是这样实现的,而静态字段初始化中没有实现这种机制(在C#中不管实例初始化和静态初始化都实现了这种机制),静态字段初始化的顺序是完全根据源代码中定义顺序来初始化的;从耦合的角度,这就是一个顺序耦合的典型。不知道为什么Java要这样实现,是否它有其他方面的问题的考虑?亦或是Java设计者或者Java编译器设计者的一个失误?不管怎么样,用sunjavac编译出来的以上程序的运行结果如下:

----------------
Static Fields
------------
staticIntVar: 
20
staticComputeIntVar: 
7
staticStrVar: Static block init(before)
staticRefVar: java.lang.Object@14318bb
staticAfterIntVar: 
30
staticAfterStrVar: Static field init(after)
-----------------
Instance Fields
---------
fieldIntVar : 
200
fieldComputeIntVar : 
8
fieldStrVar : Constructor field init(before)
fieldAfterIntVar : 
400
fieldAfterStrVar : Constructor field init(after)

 

问题解释:

从以上程序生成的二进制代码就可以很好的解释以上的结果:

<
clinit
>
:
   
//
staticIntVar = 10
     
0
 bipush 
10
     
2
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : 
int
 [
22
]
      
//
 staticComputeIntVar = (int)(Math.random() * 10)
     
5
 invokestatic java.lang.Math.random() : 
double
 [
24
]
     
8
 ldc2_w 
<
Double 
10.0
>
 [
30
]
    
11
 dmul
    
12
 d2i
    
13
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticComputeIntVar : 
int
 [
32
]
    
//
staticStrVar = “Static field init(before)”
    
16
 ldc 
<
String 
"
Static field init(before)
"
>
 [
34
]
    
18
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [
36
]
    
//
staticRefVar = new Object();
    
21
 
new
 java.lang.Object [
3
]
    
24
 dup
    
25
 invokespecial java.lang.Object() [
38
]
    
28
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticRefVar : java.lang.Object [
41
]
    
//
staticIntVar = 20
    
31
 bipush 
20
    
33
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : 
int
 [
22
]
    
//
staticStrVar = “Static block init(before)”
    
36
 ldc 
<
String 
"
Static block init(before)
"
>
 [
43
]  
    
38
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [
36
]
    
//
staticAfterIntVar = 40
    
41
 bipush 
40
    
43
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : 
int
 [
45
]  
    
//
staticAfterStr = “Statci block init(after)”
    
46
 ldc 
<
String 
"
Static block init(after)
"
>
 [
47
    
48
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [
49
]
    
//
staticAfterIntVar = 30
    
51
 bipush 
30
    
53
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : 
int
 [
45
]
    
//
staticAfterStrVar = “Static field init(after)”
    
56
 ldc 
<
String 
"
Static field init(after)
"
>
 [
51
]
    
58
 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [
49
]
    
61
 
return
 
<
init
>
:
       
//
invoke base constructor
     
0
  aload_0 [
this
]
     
1
 invokespecial java.lang.Object() [
38
]
     
4
 aload_0 [
this
]
       
//
fieldIntVar = 100
     
5
 bipush 
100
     
7
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : 
int
 [
55
]
       
//
fieldComputeIntVar = (int)(Math.random() * 100)
    
10
 aload_0 [
this
]
    
11
 invokestatic java.lang.Math.random() : 
double
 [
24
]
    
14
 ldc2_w 
<
Double 
100.0
>
 [
57
]
    
17
 dmul
    
18
 d2i
    
19
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldComputeIntVar : 
int
 [
59
]
//
fieldStrVar = “Instance field init(before)”
    
22
 aload_0 [
this
]
    
23
 ldc 
<
String 
"
Instance field init(before)
"
>
 [
61
]
    
25
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [
63
]
//
fieldAfterIntVar = 300
    
28
 aload_0 [
this
]
    
29
 sipush 
300
    
32
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : 
int
 [
65
]
//
fieldAfterStrVar = “Instance field init(after)”
    
35
 aload_0 [
this
]
    
36
 ldc 
<
String 
"
Instance field init(after)
"
>
 [
67
]
    
38
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [
69
]
//
fieldIntVar = 200
    
41
 aload_0 [
this
]
    
42
 sipush 
200
 
    
45
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : 
int
 [
55
]
//
fieldStrVar = “Constructor field init(before)”
    
48
 aload_0 [
this
]
    
49
 ldc 
<
String 
"
Constructor field init(before)
"
>
 [
71
]
    
51
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [
63
]
//
fieldAfterIntVar = 400
    
54
 aload_0 [
this
]
    
55
 sipush 
400
    
58
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : 
int
 [
65
]
//
fieldAfterStrVar = “Constructor field init(after)”
    
61
 aload_0 [
this
]
    
62
 ldc 
<
String 
"
Constructor field init(after)
"
>
 [
73
]
    
64
 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [
69
]
    
67
 
return

问题延伸

在这里,细心的人可能还会想到另外一个问题,如果StaticInitSequence类还有父类,并且父类中同同时有静态成员初始化,静态语句块初始化,实例成员初始化,构造函数初始化,那会这样的顺序会是怎么样的呢?在Java中,保证父类的初始化要早于子类的初始化,因而如果有父类存在的话,一定是先父类初始化做好以后才做子类的初始化(这一点和C#又有略微的不同,在C#中,子类的字段初始化语句要早于父类的初始化语句和构造函数),并且是先静态初始化再实例初始化。

                                                                                                                    于2010年9月24日
注:这些文章都是前些时候写的,之前博客很乱,也都是随便贴一些自己写的或转载的,还有一些则是没有贴出来过的。现在打算好好整理一下,完整的记录自己的一些学习历程,而每次看到过去的时间,则让我想起以前的日子,因而我对时间一直是很重视的,所以每篇都著名写的日期,直到最先的文章出现。:)

你可能感兴趣的文章
pycharm修改hosts文件
查看>>
iOS代理模式(delegate)的使用
查看>>
冯.诺依曼结构与哈佛结构
查看>>
类继承
查看>>
js 点名
查看>>
【文文殿下】CF1098C Construct a tree 题解
查看>>
iOS 微信支付链接
查看>>
JavaScript中的日期处理注意事项
查看>>
基于Centos7.2搭建Cobbler自动化批量部署操作系统服务
查看>>
div+css定位position详解
查看>>
iOS定位和位置信息获取
查看>>
《图解HTTP》1~53Page Web网络基础 HTTP协议 HTTP报文内的HTTP信息
查看>>
初识●美丽的邂逅
查看>>
C#各种数据类型的最大值和最小值常数
查看>>
支付常见问题
查看>>
Android基础之用Eclipse搭建Android开发环境和创建第一个Android项目(Windows平台)...
查看>>
【本地储存】修改我吧,然后刷新页面看看
查看>>
两个表,字段模糊匹配
查看>>
实用栈检查括号是否匹配
查看>>
HTML5游戏开发,剪刀石头布小游戏案例
查看>>