关于Java静态成员变量和静态初始化块等的初始化顺序的详细介绍

来源:互联网 发布:淘宝男休闲鞋 编辑:程序博客网 时间:2024/04/27 17:13
关于Java静态成员变量和静态初始化块等的初始化顺序的详细介绍


对于主动请求一个类时,JVM首先会将该类加载到内存中,先初始化该类的静态成员变量和静态初始化块。
主动请求一个类的情形:
a.调用类A的静态变量 
b.实例化类A,即new A
c.继承类A
d.使用反射的方式获取类A
e.类A是程序的入口类(即main方法所在的类)

例1 
public class StaticCode1 {
private static StaticCode1 tsc = new StaticCode1();
static{
System.out.println("4");
}
private InstanceVariable iv = new InstanceVariable();

private StaticCode1(){
System.out.println("3");
}


public static void main(String[] args){
}
}


class InstanceVariable {
static{
System.out.println("1");
}
public InstanceVariable(){
System.out.println("2");
}
}
输出:
1
2
3
4
解释:
由于类StaticCode1是程序的入口类,是主动请求类。
a.将StaticCode1加载到内存中
b.静态变量会按照声明的顺序先依次声明并设置为该类型的默认值,但不赋值为初始化的值,即stc=null;iv=null;
c.声明完毕后,再按声明的顺序依次设置为初始化的值,如果没有初始化的值就跳过.按照顺序,先初始化tsc,由于StaticCode1已经加载到内存中,故此时只需要实例化该类,
 即执行new StaticCode1();
d.对于类的初始化,在调用构造方法之前首先初始化非static的成员变量和非static初始化块。即执行private InstanceVariable iv = new InstanceVariable();
          此时类InstanceVariable类为被加载,应先加载,然后按照步骤b进行初始化static初始化块,输出1.然后调用构造函数输出2;
e.调用StaticCode1的构造函数,输出3.
f.按照顺序初始化static静态初始化块输出4.
备注:
如果在StaticCode1中的main方法中增加语句StaticCode1 s = new StaticCode1();则输出:
1
2
3
4
2
3
解释:
对于输出1234已经在上面给予介绍,由于StaticCode1类已经加载,不用在对该类进行相应的静态变量和静态初始化块进行初始化等操作。故只需要执行new StaticCode1(); 按照上述步骤d,由于InstanceVariable类也已经加载到了内存中,故直接输出2;接着输出3.


例2 (源自http://www.litefeel.com/java-static-initializtion-order/)
public class A {
public static int b = B.a;    
public static A plus =new A("A");
public static final int finalInt = (int)(Math.random()*100);
public static B p =  new B("A");
public static final String finalStr = "finalStr";
public static final Integer finalInteger = new Integer(10);  
public static int a = 1;    
public static B c = null;   
public A(String from)   
{        
System.out.println("----------- begin A::A ----------------");       
System.out.println("A::A, from="+from);        
System.out.println("A::A, A.b="+A.b);        
System.out.println("A::A, A.finalInt="+A.finalInt);        
System.out.println("A::A, B.a="+B.a);       
System.out.println("A::A, B.plus="+B.plus);       
System.out.println("----------- end A::A ----------------");   
}     

public static void main(String[] arg)  
{
System.out.println("main, A.b="+A.b);  
System.out.println("main, B.t="+B.t);    
System.out.println("main, C.a="+C.a);  
}
}


class B
{
public static int t = A.a; 
public static A plus = new A("B");
public static int a = 1;
public B(String from) 
{
System.out.println("----------- begin B::B ----------------");  
System.out.println("B::B, from="+from);       
System.out.println("B::B, B.a="+B.a);       
System.out.println("B::B, A.a="+A.a);      
System.out.println("B::B, A.p="+A.p);      
System.out.println("B::B, A.plus="+A.plus);
System.out.println("B::B, A.finalInt="+A.finalInt);
System.out.println("B::B, A.finalInteger="+A.finalInteger);     
System.out.println("B::B, A.finalStr="+A.finalStr);      
System.out.println("----------- end B::B ----------------"); 
}
}


class C
{
public static final A a = new A("C");
}
输出:
----------- begin A::A ----------------
A::A, from=B
A::A, A.b=0
A::A, A.finalInt=0
A::A, B.a=0
A::A, B.plus=null
----------- end A::A ------------------
--------- begin A::A ----------------
A::A, from=A
A::A, A.b=1
A::A, A.finalInt=0
A::A, B.a=1
A::A, B.plus=A@a90653
----------- end A::A ---------------
------------ begin B::B ----------------
B::B, from=A
B::B, B.a=1
B::B, A.a=0
B::B, A.p=null
B::B, A.plus=A@1fb8ee3
B::B, A.finalInt=61
B::B, A.finalInteger=null
B::B, A.finalStr=finalStr
----------- end B::B ----------------
main, A.b=1
main, B.t=0
----------- begin A::A ----------------
A::A, from=C
A::A, A.b=1
A::A, A.finalInt=61
A::A, B.a=1
A::A, B.plus=A@a90653
----------- end A::A ----------------
main, C.a=A@61de33
解释:
a.由于A是自主请求类,因此首先加载A,先默认初始化A类的所以static成员变量为默认值。
b.对于final的静态常量其实是遵循普通静态常量的初始化的,但是在编译时,编译器会将不可变的常量值在使用的地方替换掉。即此时
 finalStr就默认初始化为"finalStr"了
c.按照顺序初始化A中的静态变量和初始化块,在执行到b = B.a;时,类B成了自主请求类,暂停b的初始化
d.加载B类,默认初始化B类的所有static成员,然后按照顺序初始化静态变量和初始化块,执行到t=A.a;时,由于此时类A的静态成员变
 量a的值还是默认值0,因此t=0;
e.初始化B类中的plus,由于A已经被加载,因此直接对A进行初始化,由于A中不存在非static的成员变量和初始化数据块,因此直接调用
 A的构造方法,此时类A的成员变量b为默认值0,finalInt为默认值0;类B的成员变量a为默认值0,plus为默认值null.
f.继续初始化B类的静态成员变量a,初始化为1.至此,类B的加载和初始化操作完成。
g.继续完成类A的静态成员变量b的初始化任务,初始化为B.a,即为1.
h.初始化类A的plus,根据上述分析应直接调用构造方法A(String str);
i.初始化类A的finalInt为[0,100)内的任意一个随机数,假设为61
j.初始化类A的p,由于B已经加载完成,并且类中没有非static的成员变量和初始化块,应直接调用构造方法
k.继续初始化后续的finalStr、finalInteger、a、c.至此,类A的加载并且初始化静态的成员变量和初始化块操作完成。
l.执行main方法里面的语句,A.b=1,b.t=0;执行到C.a时,由于类C未加载到内存中,应首先对其进行加载并初始化等操作。
m.初始化类C的静态成员变量a。
n.输出main方法里面的C.a=A@61de33

例3 (源自阿里巴巴2014校园招聘南京站研发9月22日笔试)
public class StaticVar {
public static int k=0;
public static StaticVar t1 = new StaticVar("t1");
public static StaticVar t2 = new StaticVar("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
System.out.println("构造块");
}

public StaticVar(String str)
{
System.out.println((++k)+":"+ str +"  i=" + i +"  n=" + n);
++i;
++n;
}

static{
System.out.println("静态块");
}

public static int print(String str){
System.out.println((++k)+":"+ str + "  i=" + i +"  n=" + n);
++n;
return ++i;
}

public static void main(String[] args) {
StaticVar t = new StaticVar("init");
}
}
输出:
1:j  i=0  n=0
构造块
2:t1  i=1  n=1
3:j  i=2  n=2
构造块
4:t2  i=3  n=3
5:i  i=4  n=4
静态块
6:j  i=5  n=99
构造块
7:init  i=6  n=100

解释: