本文對[#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"