那么当有继承发生时初始化顺序是怎么样的呢?
注意以下几点:
1、static数据只在类第一次加载时执行。可以认为加载完类后的首个动作就是初始化static数据。
2、类加载:当有继承关系发生时类怎么加载呢?
先定位main函数所在的类,如果该类不存在继承关系,则加载它,并对该类static进行初始化;
如果该类存在extends关系时,则暂时不加载该类,而是继续往前定位extends所指的父类,如果在父类中再发现extends关系时,则仍然不加载该父类,而是继续往前定位它的父类。最终定位到他们的祖父类。
定位工作完成后,再逐步加载类,先加载祖父类,加载后立刻初始化祖父类的static数据。再加载祖父类的子类,实例化该子类的static数据。即 与定位工作反向进行加载。
3、按照实例化子类必先调用基类构造函数的顺序。进行祖父类的非static初始化,并调用祖父类的构造方法,再祖父类子类的非static初始化,并调用祖父类子类的构造方法
4、最后所有父类初始化完成,最后调用派生类的构造方法。
class Mill{ static Book mMill= new Book("static Mill"); private Book nmMill = new Book("no static Mill"); Mill(){ System.out.println("mill constructor"); } } class Bread extends Mill{ static Book bBread = new Book("static Bread"); private Book nbBread = new Book("no static Bread"); Bread(){ System.out.println("Bread constructor"); } } class Lanuch extends Bread{ static Book lLanuch = new Book("static Lanuch"); private Book nlLanuch = new Book("no static Lanuch"); Lanuch(){ System.out.println("Lanuch constructor"); } } class Book{ Book(String msg){ System.out.println(msg); } } public class Test { private Book k = new Book("Test Book"); static Book g = new Book("Test static Book"); public static void main(String[] args){ Lanuch l = new Lanuch(); } }
结果:
Test static Book
static Mill
static Bread
static Lanuch
no static Mill
mill constructor
no static Bread
Bread constructor
no static Lanuch
Lanuch constructor
特别注意的是
static Mill
static Bread
static Lanuch
这三个的输出顺序
结果分析说明:
程序先定位到main类Test,因为Test本身不存在extends关系,所以加载Test类,并对static初始化,输出:
Test static Book
继续执行发现:Lanuch l = new Lanuch();则定位Lanuch类,因为存在extends暂时不加载该类,还是继续定位父类,直到定位到Mill类,
之后加载Mill类,初始化static,再加载Bread类,初始化static,再加载Lanuch类,初始化static,输出:
static Mill
static Bread
static Lanuch
加载完成后,进行构造方法的调用,调用顺序为先基类再导出类,并且调用构造方法前进行非static成员的初始化,所以输出:
no static Mill
mill constructor
no static Bread
Bread constructor
no static Lanuch
Lanuch constructor
下面的示例更能体现整个初始化的过程:
class Mill{ static Book mMill= new Book("static Mill"); private Book nmMill = new Book("no static Mill"); Mill(){ System.out.println("mill constructor"); } } class Bread extends Mill{ static Book bBread = new Book("static Bread"); private Book nbBread = new Book("no static Bread"); Bread(){ System.out.println("Bread constructor"); } } class Lanuch extends Bread{ static Book lLanuch = new Book("static Lanuch"); private Book nlLanuch = new Book("no static Lanuch"); Lanuch(){ System.out.println("Lanuch constructor"); } } class Book{ Book(String msg){ System.out.println(msg); } } public class Test extends Lanuch { private Book k = new Book("Test Book"); static Book g = new Book("Test static Book"); Test(){ System.out.println("Test constructor"); } public static void main(String[] args){ //Lanuch l = new Lanuch(); Test t = new Test(); } }
输出结果为:
static Mill
static Bread
static Lanuch
Test static Book
no static Mill
mill constructor
no static Bread
Bread constructor
no static Lanuch
Lanuch constructor
Test Book
Test constructor
结果分析说明
可以看到结果并没有先输出:Test static Book
定位顺序:程序先定位Test类,发现extends,暂时不加载Test,继续定位基类,直到Mill,
加载顺序:先加载Mill类,直到Test类,按加载后立刻初始化static,所以有输出:
static Mill
static Bread
static Lanuch
Test static Book
加载完成后,进入main发现Test t = new Test();
构造顺序:先基类初始化和调用构造方法再导出类。。
所以有输出:
no static Mill
mill constructor
no static Bread
Bread constructor
no static Lanuch
Lanuch constructor
Test Book
Test constructor
所以,理解初始化顺序问题的关键是:
一、分清类的定位过程,
二、分清类的加载过程,以及加载后要进行哪类数据的初始化,这里为static
二、分清类的构造过程,以及构造进行之前要进行哪类数据的初始化,这里为非static