我们根据一段代码来分析对象初始化流程:
/**
*基类
*/
class Base{
public static int a = 10;
public int b;
static
{
System.out.println("Static Init Base " + a);
}
public Base()
{
System.out.println("Init Base " + this.b);
b = 30;
System.out.println("Init Base B After Base()" + this.b);
}
public static int c = getC();
public static int getC(){
System.out.println("Init Base C 100");
return 100;
}
static
{
System.out.println("Static Init Base DOWNC");
}
public static int d = getD();
public static int getD(){
System.out.println("Init Base D 200");
return 200;
}
}
/**
*一级子类
**/
class SuperClass extends Base{
public static int a1 = getSuperStaticNumber();
public int b1 = getSuperInstanceNumber();
public SuperClass()
{
System.out.println("Init SuperClass" + this.b1);
}
static
{
System.out.println("Static Init SuperClass" + a1);
}
public static int getSuperStaticNumber()
{
System.out.println("Static member init");
return 100;
}
public int getSuperInstanceNumber()
{
System.out.println("Instance member init");
return 200;
}
}
/**
*二级子类为测试该代码的驱动类
*/
public class SubClass extends SuperClass{
public static int a2 = getStaticNumber();
public int b2 = getInstanceNumber();
public SubClass()
{
System.out.println("Init SubClass " + this.b2);
}
public static int getStaticNumber()
{
System.out.println("Static member init Sub");
return 1000;
}
public int getInstanceNumber()
{
System.out.println("Instance member init Sub");
return 2000;
}
public static void main(String args[])
{
new SubClass();
}
static
{
System.out.println("Static Init " + a2); }
}
这段代码会有以下输出:
Static Init Base 10
Init Base C 100
Static Init Base DOWNC
Init Base D 200
Static member init
Static Init SuperClass100
Static member init Sub
Static Init 1000
Init Base 0
Init Base B After Base()30
Instance member init
Init SuperClass200
Instance member init Sub
Init SubClass 2000
1、类加载:
[1]对象在初始化过程,JVM会先去搜索该类的顶级父类,直到搜索到我们所定义的SubClass继承树上直接继承于Object类的子类,在这里就是Base类;
[2]然后JVM会先加载Base类,然后初始化Base类的静态变量,静态初始化块,静态变量和静态初始化块初始化的顺序,按照代码书写从上到下的顺序。按照这样第一句话会输出:Static Init Base 10【*:此时该类还未调用构造函数,构造函数是实例化的时候调用的】
[3]然后JVM按照继承树往下搜索,继续加载Base类的子类,直到加载完我们使用的对象所在的类。
2、实例化:
[4]类加载完了过后开始对类进行实例化操作,这个过程还是会先搜索到直接继承于Object类的子类,在这里就是Base类;
[5]JVM会实例化Base类的成员变量,然后调用Base类的构造函数;
[6]之后,JVM会递归往继承树下边进行调用,顺序还是遵循:成员变量->构造函数;
[7]最后直到SubClass类的构造函数调用完成
按照上边书写的逻辑,我们就很清楚了上边源代码的执行结果,而整个JVM初始化某个类的流程就是按照以上逻辑进行
在构造函数调用过程,有几点是需要我们留意的,这里就不提供代码实例,有兴趣的朋友可以自己去试试
[1]如果一个类的父类没有无参数构造函数,也就是说父类自定义了一个带参数的构造函数,那么系统不会提供无参数构造函数,此时子类在调用构造函数的时候必须最开始显示调用super(param),因为在构造函数调用之前系统总会先去调用父类的构造函数
[2]若一个类定义的时候没有提供构造函数,JVM会自动为该类定义一个无参数的构造函数
[3]一个类在调用构造函数的时候,JVM隐藏了一句代码super(),前提是父类未定义构造函数或者显示定义了无参构造函数;其含义就是调用父类的构造函数,如果父类的无参数构造函数被覆盖的话需要在子类构造函数中显示调用父类带参数的构造函数
[4]当类中的成员函数遇到变量的时候,会先根据变量名在函数域即局部变量范围内去寻找该变量,如果找不到才会去寻找实例变量或者静态变量,其意思可以理解为局部变量可以和实例变量或者静态变量同名,而且会在函数调用过程优先使用,这个原因在于在函数范围内,如果调用的变量是实例变量,其中前缀this.被隐藏了。
[5]java中,在使用new操作符创建一个类的实例对象的时候,实例变量会首先执行缺省初始化,即会被初始化成近零的值(如整形赋值0,字符串赋值null),如果有对成员变量赋初值,则对成员变量进行赋值,然后再进入类的构造函数。
[6]在构造函数里面,首先要检查是否有this或者super调用,this调用是完成类本身的构造函数之间的调用,super调用是完成对父类的调用。二者只能出现一个,并且只能作为构造函数的第一句出现。在调用this和super的时候实现跳转,转而执行被调用的this构造函数或者super构造函数。