(十四)成员的初始化
public class InitialOrderTest { // 静态变量 public static String staticField = "静态变量"; // 变量 public String field = "变量"; // 静态初始化块 static { System.out.println(staticField); System.out.println("静态初始化块"); } // 初始化块 { System.out.println(field); System.out.println("初始化块"); } // 构造器 public InitialOrderTest() { System.out.println("构造器"); } public static void main(String[] args) { new InitialOrderTest(); } }
运行以上代码,我们会得到如下的输出结果:
- 静态变量
- 静态初始化块
- 变量
- 初始化块
- 构造器
这与上文中说的完全符合。那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果:
class Parent { // 静态变量 public static String p_StaticField = "父类--静态变量"; // 变量 public String p_Field = "父类--变量"; // 静态初始化块 static { System.out.println(p_StaticField); System.out.println("父类--静态初始化块"); } // 初始化块 { System.out.println(p_Field); System.out.println("父类--初始化块"); } // 构造器 public Parent() { System.out.println("父类--构造器"); } } public class SubClass extends Parent { // 静态变量 public static String s_StaticField = "子类--静态变量"; // 变量 public String s_Field = "子类--变量"; // 静态初始化块 static { System.out.println(s_StaticField); System.out.println("子类--静态初始化块"); } // 初始化块 { System.out.println(s_Field); System.out.println("子类--初始化块"); } // 构造器 public SubClass() { System.out.println("子类--构造器"); } // 程序入口 public static void main(String[] args) { new SubClass(); } }
运行一下上面的代码,结果马上呈现在我们的眼前:
- 父类--静态变量
- 父类--静态初始化块
- 子类--静态变量
- 子类--静态初始化块
- 父类--变量
- 父类--初始化块
- 父类--构造器
- 子类--变量
- 子类--初始化块
- 子类--构造器
现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。
那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢?是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。我们以静态变量和静态初始化块为例来进行说明。
同样,我们还是写一个类来进行测试:
public class TestOrder { // 静态变量 public static TestA a = new TestA(); // 静态初始化块 static { System.out.println("静态初始化块"); } // 静态变量 public static TestB b = new TestB(); public static void main(String[] args) { new TestOrder(); } } class TestA { public TestA() { System.out.println("Test--A"); } } class TestB { public TestB() { System.out.println("Test--B"); } }
运行上面的代码,会得到如下的结果:
- Test--A
- 静态初始化块
- Test--B
public class Text extends Person{ static { System.out.println("im t static"); //2 } Text() { System.out.println("im t constroctor"); //4 } public static void main(String[] args) { Text t=new Text(); } } class Person { static { System.out.println("im p static "); //1 } Person() { System.out.println("im p constrocor"); //3 } }
结果:
im p static
im t static
im p constrocor
im t constroctor
解析:程序开头找到Text.main()方法,并加载Text,加载器通过extends注意到有个父类Person,于是对Person进行加载,然后父类的static初始化,接着子类的static也被初始化,等到所有有关类的static都被初始化之后,加载就完成了。接下去就是父类的成员变量、初始化块、构造方法的初始化,最后才是子类的成员变量、初始化块、构造方法的初始化。
class A { { System.out.println("im A 初始化块"); } A() { System.out.println("im A"); } } class B extends A { B() { System.out.println("im B"); } } class Text extends B { A a=new A(); Text() { System.out.println("im Text"); } public static void main(String[] args) { new Text(); } }
结果:
im A 初始化块
im A
im B
im A 初始化块
im A
im Text
解析:如果有继承关系的,首先加载类,即static的初始化,从最顶层父类开始初始化(static变量或者static区块),当所有类的静态类型都初始化完成之后,再对最顶层的父类的变量>初始化块>构造器 的顺序进行初始化,然后再对低一层的父类进行变量>初始化块>构造器 初始化,最后才到子类。
值得注意的是,初始化块的初始化可以重复,但是static初始化块的初始化只能一次。
class A { void aaa() { System.out.println("im A aaa()"); } A() { aaa(); System.out.println("im A"); } } class Text extends A { void aaa() { System.out.println("im text aaa()"); } Text() { System.out.println("im Text"); } public static void main(String[] args) { new Text(); } }
结果:
im text aaa()
im A
im Text
解析:当初始化到父类A的构造器的时候,调用了aaa()方法,可是子类重写了这个方法,所以调用的是子类的aaa()方法。
class A { void aaa() { System.out.println("im A aaa()"); } A() { aaa(); } } class Text extends A { private int i=1; void aaa() { System.out.println("i="+i); } Text(int i) { this.i=i; System.out.println("i="+i); } public static void main(String[] args) { new Text(4); } }
结果:
i=0
i=4
解析:首先从父类A类开始初始化,父类只有构造方法所以调用它,但是子类的aaa()覆盖了它所以调用的是子类的aaa(),值得疑惑的是如果我们是从父类开始初始化的话那么i此时应该是不存在的才对,然后真相是,在所有初始化动作发生之前,都会进行最基本的初始化即将分配给对象的存储空间全部初始化为二进制的零,也就是说所有类的成员属性都会被初始化为0如果是复杂类型则为null。 所以此时i是存在的且第一次初始化为0;接着到子类初始化,首先i=1这是变量初始化,接着是构造方法初始化i=4,所以结果i=4 。
注意: 很多时候变量的初始化都经历三个步骤,第一、最基本的初始化; 第二,变量的初始化; 第三、构造方法的初始化 。