类是java知识体系中很重要的基本概念。那么您对类的初始化过程又有多少了解呢?今天,我设计了一段代码,与大家共同探索一个较复杂的类的加载过程。
这段代码涵盖了成员变量(public、protected、private、static)、方法(static)、构造函数、继承的初始化过程。
目录
代码
运行结果
代码
在代码中以注释形式按照java类的初始化过程进行了各步骤的简要说明(Step xx),大家看注释就应该能了解代码的执行逻辑,有些内容进行了展开,写的有些啰嗦了。
需要说明的是,在Father类中有一个静态方法printInit,用于在程序运行时对各成员变量进行赋值,并打印提示信息。一般咱们写程序不会这么干,主要是为了能更好的展示各成员变量的赋值过程。
package com.a321.classloader; import java.util.Random; // Step 2: 初始化Father类,由于无父类,则直接初始化Father类的静态成员变量。静态成员变量的初始化顺序依据其在类中的书写顺序。 class Father { // Step 3: 初始化rand,此处不好输出,就不输出了。后续有x1代表静态成员变量的执行过程。 // 指定随机种子是为了可以形成固定的随机序列,方便复现测试。 static Random rand = new Random(37); // Step 14: 初始化Father的对象,实例化,需要按照书写顺序先初始化成员数据i,默认值为0,然后访问printInit。 // Step 16: 为Father.i赋值。 public int i = printInit("public Father.i initialized"); // Step 17: 初始化成员数据j,默认值为0,然后访问printInit。 // Step 19: 为Father.j赋值。 protected int j = printInit("protected Father.j initialized"); // Step 20: 初始化成员数据k,默认值为0,此时完成初始化所有的成员数据,开始初始化构造函数。 private int k; // Step 4: 初始化静态成员变量x1,x1的初始值为int的默认值0,此为整个类分配空间时,对所占用的存储空间初始化导致。 // 相应的对象引用就初始化为null。所以所有的成员变量都会有默认初始值。。 // 由于此处需要调用静态方法printInit进行赋值,所以初始化静态方法printInit。 // Step 6: 使用printInit的返回值为x1赋值。所有的静态成员变量已经初始化完成,开始进行子类的初始化。 static int x1 = printInit("static Father.x1 initialized"); // Step 21: 初始化构造函数。 Father(){ // Step 22: 输出各成员数据的值,此时可以注意到Father.k依旧为默认值0。 System.out.println("Father constructor1: i = " + i + ", j = " + j + ", k = " + k); // Step 23: 准备为Father.k赋值,需要先执行printInit。 // Step 25: 使用printInit的返回值为k赋值。 k = printInit("private Father.k initialized"); // Step 26: 输出各成员函数的值,此时可以注意到Father.k已经为新值。Father的构造函数执行完成,开始执行初始化子类Child的实例。 System.out.println("Father constructor2: i = " + i + ", j = " + j + ", k = " + k); } // Step 5: 初始化静态方法,此处用到了静态成员rand,由于之前已经初始化了,所以可以正常执行,否则会报空指针错误,因为rand的默认初始值为null。为Father.x1执行静态方法,赋值。 // Step 9: 为Child.x2执行静态方法,赋值。 // Step 15: 为Father.i执行静态方法,赋值。 // Step 18: 为Father.j执行静态方法,赋值。 // Step 24: 为Father.k执行静态方法,赋值。 // Step 28: 为Child.m执行静态方法,赋值。 // Step 31: 为Child.n执行静态方法,赋值。 static int printInit(String tips){ int val = rand.nextInt(66); System.out.println(tips + ": " + val); return val; } } // Step 1: 依据命令行的参数,初始化类Child,发现Child继承于Father,优先加载Father类。由于Java是单根继承,所以可以简单递归的找父类。 // Step 7: 初始化子类Child,此时父类已经完成初始化,开始子类的静态成员数据的初始化。 public class Child extends Father{ // Step 27: 初始化子类Child的实例,首先按照书写顺序初始化成员函数。执行printInit方法赋值。 // Step 29: 使用printInit的返回值为m赋值。 public int m = printInit("public Child.m initialized"); // Step 30: 初始化成员数据n,执行printInit方法赋值。 // Step 32: 使用printInit的返回值为n赋值,此时完成初始化所有的成员数据,开始执行Child的构造函数。 private int n = printInit("private Child.n initialized"); // Step 8: 初始化静态成员数据x2,默认值为0,然后初始化静态方法printInit。 // Step 10: 使用printInit的返回值为x2赋值,此时完成了子类Child的初始化。 static int x2 = printInit("static Child.x2 initialized"); // Step 33: 执行子类Child的构造函数。 public Child(){ // Step 34: 输出各成员函数的值,此时成员函数都已初始化。 System.out.println("Child constructor: m = " + m + ", n = " + n); } // Step 11: 开始执行静态成员方法main函数。 public static void main(String[] args) { // Step 12: 输出main执行的标识。 System.out.println("Child.main run"); // Step 13: 初始化子类Child的对象,发现还有父类Father,所以先初始化Father的对象。 // Step 35: 子类Child的初始化过程完成,new的过程完成,赋值给变量c。 Child c = new Child(); } }
运行结果
程序的执行结果如下
static Father.x1 initialized: 47 static Child.x2 initialized: 15 Child.main run public Father.i initialized: 30 protected Father.j initialized: 46 Father constructor1: i = 30, j = 46, k = 0 private Father.k initialized: 32 Father constructor2: i = 30, j = 46, k = 32 public Child.m initialized: 6 private Child.n initialized: 8 Child constructor: m = 6, n = 8