[#0x0023] class loading: further discussion involving steps of instance creation

  本文對[#0x0001][#0x0008][#0x000B]做統一歸納。

 

  一個類在能夠被程序使用之前,必須經歷三個準備工作(以下統稱爲類的執行):

  -> 1. loading

  -> 2. linking

      --> 2.1 verification

      --> 2.2 preparation

      --> 2.3 resolution (optional)

  -> 3. inintialization

 

  在[#0x0008][#0x000B]中,我們使用的loading (加載),其實是統指了以上三個步驟。

 

  loading指從.class文件中讀取類的binary representation(即類的Class對象)的過程。

  verfication過程驗證binary representation的結構是否正確。

  preparation過程爲類的static field申請空間並賦默認值,同時爲類的一些內部數據結構(如方法列表) 申請空間。

  resolution過程分析類中的引用。resolution過程是一個optional的過程,在resolution過程中可以有不同的loading策略,比如說,在resolve class A的時候,發現class A中有一個class B的引用,此時可以立即加載class B,也可以do nothing。

  initialization過程執行static initializer和initializer for static field (i.e. static variable initializer)。如:

private static int i = 5; //static variable initializer

//static initializer
static
{
	......
}

以下屬於initialization階段執行的代碼:

private int i = StaticFunction(); //雖然涉及到了static方法,不過field不是static,不能算是static variable initialzer

//雖然是對static field初始化,但這個initializer本身不是static,依舊不能算是static initializer
{
	StaticField = xxx;
	......
} 

 

  由於loading和linking過程是implementation-dependent,且不方便追蹤和查看,所以暫不討論loading和linking的觸發條件。以下着重討論initialization。

  initialization三原則:

  -> 1. 觸發原則:以下三種場景執行之前會觸發initialization

      --> 創建類的實例(constrcutor or Class.newInstance())

      --> 調用類的static方法(包括constructor)

      --> final的static field is used or assigned

  -> 2. 父類原則:子類initialization之前,其direct父類必須initialization,and do it recursively. (p.s. 類實現的接口無需initialization,類實現的接口的父接口亦無需如此)

  -> 3. 引發原則:如果子類initialization引發了父類的initialization,而此時父類還沒有loading和linking,則父類的loading和linking也會被引發(p.s. 我覺得子類的initialization同樣可以引發子類的loading和linking,如果loading和linking還沒有執行的話)。

 

  這裏需要着重強調的是:loading、linking和initialization都是類的行爲(class behavior) (所以initialization執行的都是static),而實例的創建(constructor or Class.newInstance())則是對象行爲(object behavior)。

 

  constructor執行的過程:

  -> 執行this() or super()

  -> 執行initializer和non-static variable initializer

  -> 執行constructor的餘下部分

 

  回頭看[#0x0008]的例子:

//@file Beetle.java

import static java.lang.System.out;

class Insect 
{
	private int i = 1;
	protected int j;
	private static int x1 = printInit("static Insect.x1 initialized");
	
	Insect() 
	{
		out.println("Insect constructor");
		out.println("i = " + i + ", j = " + j + ", x1 = " + x1);
		this.j = 2;
	}
	
	static int printInit(String s) 
	{
		out.println(s);

		return 3;
	}
}

public class Beetle extends Insect 
{
	private int k = printInit("Beetle.k initialized");
	private static int x2 = printInit("static Beetle.x2 initialized");
	
	public Beetle() 
	{
		out.println("Beetle constructor");
		out.println("j = " + j + ", k = " + k + ", x2 = " + x2);
	}
 
	public static void main(String[] args) 
	{
		Beetle b = new Beetle();
	}
}
//output:
/*
	static Insect.x1 initialized
	static Beetle.x2 initialized
	Insect constructor
	i = 1, j = 0, x1 = 3
	Beetle.k initialized
	Beetle constructor
	j = 2, k = 3, x2 = 3
*/

  -> 訪問Insect的main方法,是個static,引發Beetle的loading、linking和initialization,initialization又引發Insect的loading、linking和initialization

      --> 執行Insect的initialization,private static int x1( = 3),打印"static Insect.x1 initialized"

      --> 執行Beetle的initialization,private static int x2( = 3),打印"static Beetle.x2 initialized"

  -> 進入main(),發現constructor

  -> 隱式調用super(),轉到Insect的constructor

      --> Insect已經loading、linking和initialization了,直接執行non-static variable initializer,初始化private int i( = 1)和protected int j( = 0 by default)

      --> 執行Insect constructor的餘下部分,打印"Insect constructor"和"i = 1, j = 0, x1 = 3",然後j = 2

  -> 執行Beetle的non-static variable initializer,初始化private int k( = 3),打印"Beetle.k initialized"

  -> 執行Beetle constructor的餘下部分,打印"Beetle constructor"和"j = 2, k = 3, x2 = 3"

 

  回頭看[#0x000B]的例子:

class Glyph
{
	void draw() 
	{ 
		System.out.println("Glyph.draw()");
	}
	
	Glyph()
	{
		System.out.println("Glyph constructor");
		draw();
	}
}	

class RoundGlyph extends Glyph 
{
	private int radius = 1;

	RoundGlyph(int r)
	{
		System.out.println("before assignment in constructor, radius = " + radius);
		radius = r;
		System.out.println("RoundGlyph constructor, radius = " + radius);
	}
	
	void draw()
	{
		System.out.println("RoundGlyph.draw(), radius = " + radius);
	}
}	

public class PolyConstructors
{
	public static void main(String[] args)
	{
		new RoundGlyph(5);
	}
}
//Output:
/*
	Glyph constructor
	RoundGlyph.draw(), radius = 0
	before assignment in constructor, radius = 1
	RoundGlyph constructor, radius = 5
*/

  -> 訪問PolyConstructors的main方法,loading、linking PolyConstructors,進入main,發現RoundGlyph構造器

  -> 隱式調用super(),打印"Glyph constructor",執行RoundGlyph的draw()方法,打印"RoundGlyph.draw(), radius = 0" (此時還沒有執行到RoundGlyph的non-static variable initializer)

  -> 執行RoundGlyph的non-static variable initializer,radius = 1

  -> 執行RoundGlyph構造器的餘下部分,打印"before assignment in constructor, radius = 1",然後radius = 5,打印"RoundGlyph constructor, radius = 5"

發佈了49 篇原創文章 · 獲贊 2 · 訪問量 1593
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章