类接口初始化,和创建对象的顺序

1 JLS 12.4.1 p371

 

   (1)在一个类被初始化(initialize)之前,他的所有的父类先执行初始化(如果他们还没有被初始化)。 

   (2)没有初始化class One 是因为 The class One is never initialized, because it not used actively and therefore is never linked to. 只执行Load没有执行Link。

package arkblue.lang.initialclass;

class Super {
	static {
		System.out.println("Super ");
	}
}

class One {
	static {
		System.out.println("One ");
	}
}

class Two extends Super {
	static {
		System.out.println("Two ");
	}
}

class Test1 {
	public static void main(String[] args) {
		One o = null;		// 没有被激活
		Two t = new Two();
		System.out.println((Object) o == (Object) t);
	}
}

 

 

 

 

 

 

 

2 JSL 4.14.5

     在连接阶段的子阶段prepare,创建静态域,并为静态域赋默认值。

     在类的initialize阶段为静态域赋具体的值。

 

package arkblue.jsl.no4c_12_5;

class Point {
	static int npoints;
	static int mpoints = 4;
	int x, y;
	Point root;
}

class Test {
	public static void main(String[] args) {
		// 连接(Link)阶段的prepare阶段,创建静态域并赋默认值
		System.out.println("npoints=" + Point.npoints);
		// 初始化(initialize)阶段,执行类初始化操作(static)
		System.out.println("npoints=" + Point.mpoints);
		Point p = new Point();
		System.out.println("p.x=" + p.x + ", p.y=" + p.y);
		System.out.println("p.root=" + p.root);
	}
}

 

结果

 

npoints=0
npoints=4
p.x=0, p.y=0
p.root=null

 

3  JLS 12.4.1 A reference to a class field causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

引用一个类/接口的所声明的静态域(class field),即使是通过它的子类或者实现引用静态域,都会导致类初始化。

 

package arkblue.jsl.no12_4_1_e2;

class Super {
	static int taxi = 1729;
}

class Sub extends Super {
	static {
		System.out.print("Sub ");
	}
}

class Test {
	public static void main(String[] args) {
		System.out.println(Sub.taxi);
	}
}

 

 

结果

 

1729

 

 4  jsl 12.4.1 Initialization of an interface does not, of itself, cause initialization of any of its superinterfaces.

接口的初始化不会引起他的父接口的初始化。

 

package arkblue.jsl.no12_4_1_e3;

interface I {
	int i = 1;
	int ii = Test.out("ii", 2);
}

interface J extends I {
	int j = Test.out("j", 3);
	int jj = Test.out("jj", 4);
}

interface K extends J {
	int k = Test.out("k", 5);
}

class Test {
	public static void main(String[] args) {
		System.out.println(J.i);
		System.out.println(K.j);
	}

	static int out(String s, int i) {
		System.out.println(s + "=" + i);
		return i;
	}
}

 

结果

 

1
j=3
jj=4
3

 

 

5 JSL 12.5 Creation of New Class Instances 创建类的实例

 

(1)First, space is allocated for the Class Instance. All these fields are then initialized to their default values (in this case, 0 for each field). 分配空间给变量,包括hide的所有父类的变量为默认值。

(2)在自己的构造函数第一句,找this()或者super(),调用本身的其他构造器或者父类的构造器。一直递归到Object才终止。

(3)对于执行递归最上层的那个类,Next, any instance initializers, instance variable initializers of Object are invoked. 执行实例变量初始化器和实例初始化器,执行

(4)递归执行子类的实例变量初始化器和实例初始化器,然后执行构造器。

 

6  JLS 12.5 p324 the Java programming language does not specify altered rules for
method dispatch during the creation of a new class instance. If methods are
invoked that are overridden in subclasses in the object being initialized, then these
overriding methods are used, even before the new object is completely initialized.

 

package arkblue.jsl.no12_5_e2;

class Super {
	Super() {
		printThree();
	}

	void printThree() {
		System.out.println("three");
	}
}

class Test extends Super {
	int three = (int) Math.PI; // That is, 3

	public static void main(String[] args) {
		Test t = new Test();
		t.printThree();
	}

	void printThree() {
		System.out.println(three);
	}
}

 

 

结果

 

0
3

 

分析 1. 分配空间

        2. 调用子类的构造函数--默认构造函数。

        3. 调用父类的构造函数--默认构造函数。

        4. 调用Object的构造函数,执行Object的实例初始化器,执行Object的构造函数。

        5. 执行父类Super 的实例初始化器,然后执行父类Super 的构造器,调用方法printThree(),发现被子类override,执行子类的printThree()方法,这时Test 的实例初始化器还没有执行,所以字段被赋于默认值0或者null;打印 0。

        6. 执行Test 的实例初始化器,字段three 被赋值为3,然后执行Test  的构造器。

        7. 返回结果,新鲜的对象。

        8. 执行printThree()方法,打印3.

 

7 java puzzler 49

   要当心类初始化循环

 

    要想改正一个类初始化循环,需要对类的静态域的初始器进行排序,使得每一个初始化器都出现在任何依赖他的初始化器之前。

package arkblue.lang;

import java.util.Calendar;

public class Elvis {
	public static final Elvis INSTANCE = new Elvis();
	private int beltSize = 9;
	private static final int CURRENT_YEAR = Calendar.getInstance().get(
			Calendar.YEAR);

	private Elvis() {
		beltSize = CURRENT_YEAR - 1930;
	}

	public int beltSize() {
		return beltSize;
	}

	public static void main(String[] args) {
		System.out.println("Elvis wears a size " + INSTANCE.beltSize()
				+ " belt.");
	}
}

 

 

结果

 

Elvis wears a size -1930 belt.

 

 

分析:

     1 Elvis类的初始化是由虚拟机对main方法的调用引起的。

     2 静态域设置为初始值,INSTANCE  为null,CURRENT_YEAR设置为0

     3 静态域初始器按照出现的顺序执行,第一个静态域是INSTANCE ,通过调用构造器计算得到

     4 这时使用了new 关键字,为对象分配空间,beltSize 设置为默认值0;然后执行对象实例域beltSize 的初始化,初始化为9;然后执行构造器,这个构造器会用一个涉及静态域CURRENT_YEAR的表达式初始化实例变量。通常读取一个静态域是引起类被初始化的原因之一,但是由于已经在初始化Elvis了(第一步),递归的初始化尝试被忽略掉。因此,CURRENT_YEAR的值还是0,beltSize 赋值成-1930.

     5 初始化静态域CURRENT_YEAR,假设当前是2010年,则CURRENT_YEAR 被赋值为2010

     

    8 java puzzler 51

    (1)creat a class instance 的时候,分配空间给类和所有父类的字段,包括已经hide的字段。

    (2)instance variable iniitializer 的执行在构造器执行之前

    (3)千万不要在构造器中调用克复写的方法。

 

package arkblue.lang;

class Point {
	protected final int x, y;
	private final String name; // Cached at construction time

	Point(int x, int y) {
		this.x = x;
		this.y = y;
		name = makeName();
	}

	protected String makeName() { // (3)调用子类方法
		return "[" + x + "," + y + "]";
	}

	public final String toString() {
		return name;
	}
}

public class ColorPoint extends Point {
	// 在父类构造器执行后,子类构造器执行前,才对实例变量赋值color,此时color还是null
	private final String color;

	ColorPoint(int x, int y, String color) {
		super(x, y); // (2)Chain to super constructor
		this.color = color;
	}

	protected String makeName() {
		// 执行 父类方法
		return super.makeName() + ":" + color;
	}

	public static void main(String[] args) {
		// (1) invoke subclass constructor
		System.out.println(new ColorPoint(4, 2, "purple"));
	}
}

 

结果

 

[4,2]:null

 

修改 Point

 

class Point {
	protected final int x, y;
	private String name; // lazily initialized

	Point(int x, int y) {
		this.x = x;
		this.y = y;

	}

	protected String makeName() { // (3)调用子类方法
		return "[" + x + "," + y + "]";
	}

	public final synchronized String toString() {
		if (null == name) {
			name = makeName();
		}
		return name;
	}
}

 

执行main得到结果

[4,2]:purple

 

 9 java puzzler 52 类的初始化

 

package arkblue.lang.javapuzzler.n52;

class Cache {
	static {
		initializeIfNecessary();
	}
	private static int sum;

	public static int getSum() {
		initializeIfNecessary();
		return sum;
	}

	private static boolean initialized = false;

	private static synchronized void initializeIfNecessary() {
		if (!initialized) {
			for (int i = 0; i < 100; i++)
				sum += i;
			initialized = true;
		}
	}
}

public class Client {
	public static void main(String[] args) {
		System.out.println(Cache.getSum());
	}
}

 

结果

9900

 分析:

 (1)在可以调用Clien.main之前,VM必须先加载、连接、初始化Client类。

 (2)Client.main调用了Cache.getSum,VM必须先加载、连接、初始化Cache。

 (3)类初始化器按照在源码中的顺序执行,先执行静态块:调用initializeIfNecessary方法,initializeIfNecessary引用到了静态域sum和initialized ,sum的默认值是0,initialized 还没有执行初始化器所以没有被赋值,具有默认值false。

 (4)执行initializeIfNecessary方法,结束后,sum = 4950, initialized = true;

 (5)执行静态初始器initialized 赋值为false,

 (6)完成类Cache初始化,但是sum包含正确的缓冲值(4950)

 (7)执行方法Cache.getSum,此时sum=4950;initialized =false;该循环又给sum加上了4950,得到结果9900。

 

修改后:

package arkblue.lang.javapuzzler.n52;

class NewCache {
	private static final int sum = computeSum();

	private static int computeSum() {
		int result = 0;
		for (int i = 0; i < 100; i++)
			result += i;
		return result;
	}

	public static int getSum() {
		return sum;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值