在《thinking in java》里的4.4.2中,提到初始化的顺序:在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,那些变量仍会在调用任何方法之前得到初始化——甚至在构建器调用之前。
- class Tag {
- Tag(int marker) {
- System.out.println("Tag(" + marker + ")");
- }
- }
- class Card {
- Tag t1 = new Tag(1); // Before constructor
- Card() {
- // Indicate we're in the constructor:
- System.out.println("Card()");
- t3 = new Tag(33); // Re-initialize t3
- }
- Tag t2 = new Tag(2); // After constructor
- void f() {
- System.out.println("f()");
- }
- Tag t3 = new Tag(3); // At end
- }
- public class OrderOfInitialization {
- public static void main(String[] args) {
- Card t = new Card();
- t.f(); // Shows that construction is done
- }
- }
个人理解:先从入口方法main方法所在的类开始。首先创建Card型对象t,调用构造器Card()。但是在调用构造器之前,会先初始化Card对象所需要的属性:t1,t2,t3,虽然他们处于Card类的不同位置。当t1,t2,t3三个对象初始化完成后,才真正调用构造器Card()。在构造器里,t3又被初始化了一遍。t初始化完成后,进入下一条语句,调用t的方法f()。这样,系统输出的文本应该就是
- Tag(1)
- Tag(2)
- Tag(3)
- Card()
- Tag(33)
- f()
然后,关于静态变量的初始化顺序,《Thinking in java》中说道:若数据是静态的(static),那么同样的事情就会发生;如果它属于一个基本类型(主类型),而且未对其初始化,就会自动获得自己的标准基本类型初始值;如果它是指向一个对象的句柄,那么除非新建一个对象,并将句柄同它连接起来,否则就会得到一个空值(NULL)。如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static 值只有一个存储区域,所以无论创建多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面这个例子可将这个问题说更清楚一些:
- class Bowl {
- Bowl(int marker) {
- System.out.println("Bowl(" + marker + ")");
- }
- void f(int marker) {
- System.out.println("f(" + marker + ")");
- }
- }
- class Table {
- static Bowl b1 = new Bowl(1);
- Table() {
- System.out.println("Table()");
- b2.f(1);
- }
- void f2(int marker) {
- System.out.println("f2(" + marker + ")");
- }
- static Bowl b2 = new Bowl(2);
- }
- class Cupboard {
- Bowl b3 = new Bowl(3);
- static Bowl b4 = new Bowl(4);
- Cupboard() {
- System.out.println("Cupboard()");
- b4.f(2);
- }
- void f3(int marker) {
- System.out.println("f3(" + marker + ")");
- }
- static Bowl b5 = new Bowl(5);
- }
- public class StaticInitialization {
- public static void main(String[] args) {
- System.out.println(
- "Creating new Cupboard() in main");
- new Cupboard();
- System.out.println(
- "Creating new Cupboard() in main");
- new Cupboard();
- t2.f2(1);
- t3.f3(1);
- }
- static Table t2 = new Table();
- static Cupboard t3 = new Cupboard();
- }
同样,先从入口方法main方法所在的类开始。由于静态变量必须先构造(纯属个人理解),所以,Table型的对象t2是最新被构造的。调用构造器Table()。在Table类中,由于声明了静态变量b1与b2,所以先调用Bowl的构造器来创建初始化b1与b2。b1与b2初始化完成后,再调用Table的构造器。同时在Table的构造器中,通过b2调用方法f(),返回的是一个字符串。这样,t2构造完毕后,创建下一个静态变量,Cupboard类型的对象t3。同理,先调用t3的构造器Cupboard(),但是在Cupboard类中,有两个静态变量b4与b5,所以会先初始化b4与b5。但是,在调用构造器之前,还有一步,就是初始化Cupboard对象的属性,这里是一个Bowl型对象b3,所以b3会在调用Cupboard构造器之前被初始化。接着,调用构造器Cupboard用来初始化t3。在构造器中,通过b4调用了方法f(),结果也是输出一串字符串。在t3被初始化完成后,main方法所在的类的所有静态变量都被初始化了,接下来进入main方法。首先是输出一条字符串,然后是创建一个Cupboard对象。这样又要调用Cupboard的构造器。但是由于之前已经初始化了Cupboard类的静态变量b4与b5,所以不会再初始化。但是由于b3属于Cupboard对象的一个属性,所以,要先初始化b3,然后再调用构造器Cupboard()。下两条语句同样执行上述操作。最后通过t2调用方法f2,t3调用方法f3,这样所有的初始化工作就结束了。输出的结果应该是:
- Bowl(1)
- Bowl(2)
- Table()
- f(1)
- Bowl(4)
- Bowl(5)
- Bowl(3)
- Cupboard()
- f(2)
- Creating new Cupboard() in main
- Bowl(3)
- Cupboard()
- f(2)
- Creating new Cupboard() in main
- Bowl(3)
- Cupboard()
- f(2)
- f2(1)
- f3(1)
总结:要初始化一个对象,编译器首先会找到找到这个对象相应的类,然后首先初始化相应类中的static对象,接来下才是初始化非static对象(作为对象的属性的变量),在接下来才是调用构造器。
总结如果有错误,欢迎大家指出。