1、初始化顺序

在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,
那些变量仍会在调用任何方法之前得到初始化——甚至在构建器调用之前。例如:


class Tag {
	Tag(int marker) {
		System.out.println("Tag(" + marker + ")");
	}
}

class Card {
	Tag t1 = new Tag(1);	// Before constructor
	Card() {
		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		
	}
}

在Card 中,Tag 对象的定义故意到处散布,以证明它们全都会在构建器进入或者发生其他任何事情之前得到


初始化。除此之外,t3 在构建器内部得到了重新初始化。它的输入结果如下:


JAVA 类变量 初始化 父类 java变量初始化顺序_Java编程思想


因此,t3 句柄会被初始化两次,一次在构建器调用前,一次在调用期间(第一个对象会被丢弃,所以它后来

可被当作垃圾收掉)。从表面看,这样做似乎效率低下,但它能保证正确的初始化。



2、静态数据的初始化


对于静态数据,如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static 值只有一个存
储区域,所以无论创建多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面这个例子可
将这个问题说更清楚一些:


class Bow1 {
	
	Bow1(int marker) {
		System.out.println("Bow1(" + marker + ")");
	}
	
	void f(int marker) {
		System.out.println("f(" + marker + ")");
	}
}

class Table {
	static Bow1 b1 = new Bow1(1);
	Table() {
		System.out.println("Table()");
		b2.f(1);
	}
	void f2(int marker) {
		System.out.println("Table()");
	}
	static Bow1 b2 = new Bow1(2);
}

class Cupboard {
	Bow1 b3 = new Bow1(3);
	static Bow1 b4 = new Bow1(4);
	Cupboard() {
		System.out.println("Cupboard()");
		b4.f(2);
	}
	void f3(int marker) {
		System.out.println("f3(" + marker + ")");
	}
	static Bow1 b5 = new Bow1(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();
}


Bowl 允许我们检查一个类的创建过程,而Table 和Cupboard 能创建散布于类定义中的Bowl 的static 成

员。注意在static 定义之前,Cupboard 先创建了一个非static 的Bowl b3。它的输出结果如下:


JAVA 类变量 初始化 父类 java变量初始化顺序_java_02


static 初始化只有在必要的时候才会进行。如果不创建一个Table 对象,而且永远都不引用Table.b1 或
Table.b2,那么static Bowl b1 和b2 永远都不会创建。然而,只有在创建了第一个Table 对象之后(或者
发生了第一次static 访问),它们才会创建。在那以后,static 对象不会重新初始化。


初始化的顺序是首先static(如果它们尚未由前一次对象创建过程初始化),接着是非static 对象。大家
可从输出结果中找到相应的证据。


在这里有必要总结一下对象的创建过程。请考虑一个名为Dog 的类:
(1) 类型为Dog 的一个对象首次创建时,或者Dog 类的static 方法/static 字段首次访问时,Java 解释器
必须找到Dog.class(在事先设好的类路径里搜索)。
(2) 找到Dog.class 后(它会创建一个Class 对象),它的所有static 初始化模块都会运
行。因此,static 初始化仅发生一次——在Class 对象首次载入的时候。
(3) 创建一个new Dog()时,Dog 对象的构建进程首先会在内存堆(Heap)里为一个Dog 对象分配足够多的存
储空间。
(4) 这种存储空间会清为零,将Dog 中的所有基本类型设为它们的默认值(零用于数字,以及boolean 和
char 的等价设定)。
(5) 进行字段定义时发生的所有初始化都会执行。
(6) 执行构建器。




3、明确进行的静态初始化


class Cup {
	Cup(int marker) {
		System.out.println("Cup(" + marker + ")");
	}
	void f(int marker) {
		System.out.println("f(" + marker + ")");
	}
}

class Cups {
	/**
	 * 尽管看起来象个方法,但它实际只是一个static 关键字,后面跟随一个方法主体。
	 * 与其他static 初始化一样,这段代码仅执行一次——首次生成那个类的一个对象时,
	 * 或者首次访问属于那个类的一个static 成员时
	 */
	static Cup c1;
	static Cup c2;
	static {
		c1 = new Cup(1);
		c2 = new Cup(2);
	}
	Cups() {
		System.out.println("Cups()");
	}
}

public class ExplicitStatic {

	public static void main(String[] args) {
		System.out.println("Inside main()");
		Cups.c1.f(99);	// (1)
	}
	static Cups x = new Cups();	// (2)
	static Cups y = new Cups(); // (2)
}

在标记为(1)的行内访问static 对象c1 的时候,或在行(1)标记为注释,同时(2)行不标记成注释的时候,用于Cups 的static 初始化模块就会运行。若(1)和(2)都被标记成注释,则用于Cups 的static 初始化进程永远不会发生。

4、非静态实例的初始化


class Mug {
	Mug(int marker) {
		System.out.println("Mug(" + marker + ")");
	}
	void f(int marker) {
		System.out.println("f(" + marker + ")");
	}
}

public class Mugs {

	Mug c1;
	Mug c2;
	{
		c1 = new Mug(1);
		c2 = new Mug(2);
		System.out.println("c1 & c2 initialized");
	}
	Mugs() {
		System.out.println("Mugs()");
	}
	public static void main(String[] args) {
		System.out.println("Inside main()");
		Mugs x = new Mugs();
	}
}


大家可看到实例初始化从句:

{
 c1 = new Mug(1);
 
c2 = new Mug(2);
 System.out.println("c1 & c2 initialized");
 }


它看起来与静态初始化从句极其相似,只是static 关键字从里面消失了。为支持对“匿名内部类”的初始化,必须采用这一语法格式。