Static变量和实例变量的初始化顺序问题

问题重现

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

 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 编译器设计者的一个失误?不管怎么样,用 sun javac 编译出来的以上程序的运行结果如下:

---------------- 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
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值