Java提供了两种不同的初始化类型,各自是类的初始化和对象的初始化。
类成员都是静态的,默认会设置一个值。对象的初始化会在构造函数里面进行。但假设想要赋给静态变量非默认值。或者是初始化一类共同拥有的对象属性(不论调用哪个构造函数)。那么就须要一些特殊的方法。提供了静态初始化块和非静态初始化块来处理这两种情况.
-
静态初始化块
静态初始化块是通过static{}来定义的。一个简单的代码示比例如以下:
public class CorderStatic { staticint idx; static{ System.out.println("idx: " + Integer.toString(idx++)); System.out.println("idx: " + Integer.toString(idx)); } publicstatic void main(String[] argv) { System.out.println("DONE"); } }代码执行的结果是:
idx: 0 idx: 1 DONE |
从输出结果能够看出静态初始化块在静态成员的初始化后调用。并且假设类中存在多个静态初始化块。则依据出现的次序进行调用。
-
非静态初始化块
非静态初始化块定义在{}块中。其与静态初始化块的差别是没有statickeyword修辞。
測试代码例如以下:
public class CorderInstance { int idx; { System.out.println("idx: " + Integer.toString(idx++)); System.out.println("idx: " + Integer.toString(idx++)); } publicCorderInstance() { System.out.println("idxin constructor : " + Integer.toString(idx)); } public static void main(String[] argv) { newCorderInstance(); System.out.println("DONE"); } }
代码输出例如以下:
idx: 0 idx: 1 idxin constructor : 2 DONE |
可见,非静态初始化块在对象属性初始化结束。构造函数调用前被调用。
-
初始化顺序
假设一个类里面定义了静态和非静态的初始化块。哪个会先被运行呢?測试代码例如以下:
public class Corder { static int staticpro; static{ System.out.println("staticblock : " + Integer.toString(staticpro)); } int instancepro; { System.out.println("non-staticblock." + Integer.toString(instancepro++)); } publicCorder() { System.out.println("instanceproperty : " + Integer.toString(instancepro++)); System.out.println("Theconstructor had been complete."); } publicstatic void main(String[] argv) { newCorder(); } }
代码输出例如以下:
staticblock : 0 non-staticblock.0 instanceproperty : 1 Theconstructor had been complete. |
可见,静态初始化块会先被调用。然后是非静态初始化块,最后是构造函数。
-
继承与初始化
初始化块在遇到类继承时。情况要变得复杂一些。
先把測试代码贴出来:
public class CorderInherit { static int staticpro; staticA a; static{ System.out.println("staticinitialization : " + Integer.toString(staticpro)); if(a == null) { System.out.println("classa is null."); } a= new B(10); } intinstancepro; A aa; { instancepro= 10; System.out.println("specialblock." + Integer.toString(instancepro)); aa= new B(10); } publicCorderInherit(int i) { System.out.println("instanceproperty : " + Integer.toString(instancepro)); instancepro= i; System.out.println("staticproperty : " + Integer.toString(staticpro)); System.out.println("instanceproperty : " + Integer.toString(instancepro)); System.out.println("Theconstructor had been complete."); } publicstatic void main(String[] argv) { newCorderInherit(-1); } } class A { static int index; static{ System.out.println("ClassA static 1 " + Integer.toString(index++)); } { System.out.println("ClassA special 1 " + Integer.toString(index++)); } public A() { System.out.println("constructclass A." + Integer.toString(index++)); } static{ System.out.println("classA static 2 " + Integer.toString(index++)); } { System.out.println("ClassA Special 2 " + Integer.toString(index++)); } } class B extends A { static int index; static{ System.out.println("ClassB static 1 " + Integer.toString(index++)); } { System.out.println("ClassB special 1 " + Integer.toString(index++)); } public B(int i) { System.out.println("constructclass B." + Integer.toString(index++)); } static{ System.out.println("classB static 2 " + Integer.toString(index++)); } { System.out.println("ClassB Special 2 " + Integer.toString(index++)); } }
代码输出例如以下:
staticinitialization : 0 classa is null. ClassA static 1 0 classA static 2 1 ClassB static 1 0 classB static 2 1 ClassA special 1 2 ClassA Special 2 3 constructclass A.4 ClassB special 1 2 ClassB Special 2 3 constructclass B.4 specialblock.10 ClassA special 1 5 ClassA Special 2 6 constructclass A.7 ClassB special 1 5 ClassB Special 2 6 constructclass B.7 instanceproperty : 10 staticproperty : 0 instanceproperty : -1 Theconstructor had been complete. |
观察上面的输出,能够看出,当实例化B的对象时,虚拟机会载入B.class。但由于B继承A。所以A.class也会被载入。这就导致A和B的静态初始化块被分别调用。
构造B的对象时,A的非静态初始化块和默认构造函数都会被调用,然后才是B的非静态初始化块和构造函数。
-
结论
通过上面的样例能够看出,类的完整初始化顺序例如以下(子类为当前类,或者要new的类):
-
给父类的静态成员赋默认值
-
按出现顺序调用父类的静态初始化块
-
给子类的静态成员赋默认值
-
按出现顺序调用子类的静态初始化块
-
赋给父类对象属性默认值
-
按顺序调用父类对象的非静态初始化块
-
调用父类构造函数
-
赋给子类对象属性默认值
-
按顺序调用子类对象的非静态初始化块
-
调用子类构造函数