Java中程序初始化的順序
1,在一個類的內部(不考慮它是另一個類的派生類):很多人認為,類的成員變量是在構造方法調用之後再初始化的,先不考慮這種觀點的正確性,先看一下下面的代碼:
[java] view plaincopy
- class Test01...{
- public Test01(int i)...{
- System.out.println("Test01 of constractor : " + i);
- }
- }
- public class Test02 ...{
- private Test01 t1 = new Test01(1);
- private int n = 10;
- public Test02()...{
- System.out.println("Test02 of constructor : " + n);
- }
- private Test01 t2 = new Test01(2);
- public static void main(String[] args) ...{
- Test02 test = new Test02();
- }
- }
- 輸出的結果為:
- Test01 of constractor : 1
- Test01 of constractor : 2
- Test02 of constructor : 10
通過輸出,可見當生成Test02的實例test時,它並不是首先調用其構造方法而是先是成員變量的初始化,而且成員的初始化的順序以成員變量的定義順序有關,先定義的先初始化,初始化後再調用構造方法。其實成員變量的初始化,在類的所有方法調用之前進行,包括構造方法
當類中有Static 修飾的成員呢?測試下面一段代碼:
- public class Test03 ...{
- private int i1 = printCommon();
- private static int i2 = printStatic();
- public Test03()...{
- }
- public static int printCommon()...{
- System.out.println("i1 is init!");
- return 1;
- }
- public static int printStatic()...{
- System.out.println("i2 is init!");
- return 2;
- }
- public static void main(String[] args) ...{
- Test03 t = new Test03();
- }
- }
- 輸出結果為:
- i2 is init!
- i1 is init!
可見static的成員比普通的成員變量先初始化。
我們都知道,如果一個類的成員變量沒有在定義時,系統會給予系統默認的值,有=號的就直接給予右值,系統在給予初值和=號給予值這2中方式,在執行時間上有先後嗎?為了測試,我編寫了如下代碼:
- public class Test04 ...{
- private static Test04 t1 = new Test04();
- private static int i1;
- private static int i2 = 2;
- public Test04()...{
- i1++;
- i2++;
- }
- public static void main(String[] args) ...{
- Test04 t2 = new Test04();
- System.out.println("t2.i1 = " + t2.i1);
- System.out.println("t2.i2 = " + t2.i2);
- }
- }
- 我們先預計一下輸出,可能有幾種答案:2和3,3和3,2和2
- 執行代碼後:
- t2.i1 = 2
- t2.i2 = 3
為什麽是2和3呢?其實代碼的執行順序是這樣的:首先執行給t1,i1,i2分別給予初始值null,0,0,再執行
Test04 t1 =new Test04(),這樣i1++,i2++被執行,i1,i2都變為1,執行完畢後接著執行int i1; i1,i2的值仍然是1,1,當執行int i2 = 2時i2被賦予了值,即i1 = 1,i2=2;再執行Test04 t2 = new Test04(),i1,i2再執行++,此時i1 =2,i2 =3,輸出i1,i2,結果就是:t2.i1 = 2,t2.i2 = 3。 通過上面的代碼我們可以認為系統默認值的給予比通過等號的賦予先執行。
2,一個類還有上層的類,即父類:
當生成一個子類時,大家到知道會調用父類的構造方法。如果子類和父類中都有Static的成員變量呢,其實我們在深入分析一個類的內部初始化後,對於存在父類的類的初始化其實原理都一樣,具體以下面的代碼為例:
- class SuperClass ...{
- static...{
- System.out.println("SuperClass of static block");
- }
- public SuperClass()...{
- System.out.println("SuperClass of constracutor");
- }
- }
- public class SubClass extends SuperClass...{
- static...{
- System.out.println("SubClass of static block");
- }
- public SubClass()...{
- System.out.println("SubClass of constracutor");
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClass();
- }
- }
- 輸出結果:
- SuperClass of static block
- SubClass of static block
- SuperClass of constracutor
- SubClass of constracutor
可見當父類,和子類有Static時,先初始化Static,再初始化子類的Static,再初始化父類的其他成員變量->父類構造方法->子類其他成員變量->子類的構造方法。
父類上層還有父類時,總是先執行最頂層父類的Static-->派生類Static-->派生類Static-->.......-->子類Static-->頂層父類的其他成員變量-->父類構造方法--> 派生類的其他成員變量 --> 派生類構造方法--> ...............-->子類其他成員變量-->子類構造方法
討論到繼承,就不得提一下多態:
如果父類構造方法的代碼中有子類中被重寫得方法,當執行這樣的語句
SuperClass super = new SubClass();
初始化時調用父類的構造方法,是執行父類的原方法,還是執行子類中被重寫的方法呢?
- class SuperClass...{
- public SuperClass()...{
- System.out.println("SuperClass of constructor");
- m();
- }
- public void m()...{
- System.out.println("SuperClass.m()");
- }
- }
- public class SubClassTest extends SuperClass ...{
- private int i = 10;
- public SubClassTest()...{
- System.out.println("SubClass of constructor");
- super.m();
- m();
- }
- public void m()...{
- System.out.println("SubClass.m(): i = " + i);
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClassTest();
- }
- }
- 可能很多人會認為輸出為:
- SuperClass of constructor
- SubClass.m(): i = 10
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 其實不然!
- 正確輸出為:
- SuperClass of constructor
- SubClass.m(): i = 0
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 在生成對象時,父類調用的M()方法,不是父類的 M()方法,而時子類中被重寫了的M()方法!!並且還出現一個怪異的現象,子類的privte int i 也被父類訪問到,這不是和我們說private的成員只能在本類使用的原則相違背了嗎?其實我們說的這條原則是編譯期間所遵守的,在JAVA程序的編譯期間,它只檢查語法的合法性,在JAVA的JVM中,即運行期間,不管你聲明的什麽,對於JVM來說都是透明的,而多態是在運行期間執行的,所以能拿到SubClass的private成員,一點都不奇怪,只是此時還沒執行 i = 10,所以在父類的構造方法中調用m()時,系統只能將i賦予系統初值0。
- 下面是我設計的一道完整的初始化例子,可測試你對類的初始化問題是否完整掌握:
- 寫出程序運行的結果:
- class A...{
- private int i = 9;
- protected static int j;
- static...{
- System.out.println("-- Load First SuperClass of static block start!-- ");
- System.out.println("j = " + j);
- System.out.println("-- Load First SuperClass of static block End -- ");
- }
- public A()...{
- System.out.println("------- Load SuperClass of structor start --------");
- System.out.println("Frist print j = " + j);
- j = 10;
- m();
- System.out.println("k = " + k);
- System.out.println("Second print j = " + j);
- System.out.println("----------- Load SuperClass End ----------- ");
- }
- private static int k = getInt();
- public static int getInt()...{
- System.out.println("Load SuperClass.getInt() ");
- return 11;
- }
- static...{
- System.out.println("--- Load Second SuperClass of static block!-------");
- System.out.println("j = " + j);
- System.out.println("k = " + k);
- System.out.println("-- Load Second SuperClass of static block End -- ");
- }
- public void m()...{
- System.out.println("SuperClass.m() , " + "j = " +j);
- }
- }
- class B extends A ...{
- private int a = 10;
- static...{
- System.out.println("---- Load SubClass of static block!------");
- System.out.println("-- Load SubClass of static block End -- ");
- }
- public B()...{
- System.out.println("Load SubClass of structor");
- m();
- System.out.println("--- Load SubClass End ---- ");
- }
- public void m()...{
- System.out.println("SubClass.m() ," + "a = " + a );
- }
- }
- public class Test1...{
- public static void main(String[] args)...{
- A a = new B();
- }
- }
- 正確的答案為:
- -- Load First SuperClass of static block start!--
- j = 0
- -- Load First SuperClass of static block End --
- Load SuperClass.getInt()
- --- Load Second SuperClass of static block!-------
- j = 0
- k = 11
- -- Load Second SuperClass of static block End --
- ---- Load SubClass of static block!------
- -- Load SubClass of static block End --
- ------- Load SuperClass of structor start --------
- Frist print j = 0
- SubClass.m() ,a = 0
- k = 11
- Second print j = 10
- ----------- Load SuperClass End -----------
- Load SubClass of structor
- SubClass.m() ,a = 10
- --- Load SubClass End ----
下面需要說明的一點也是至關重要的一點:那就是成員變量的初始化和非static初始化塊之間的執行順序是按照他們出現的先後順序來執行的
[java] view plaincopy
- public class Test04
- {
- //下面的這兩行代碼放置的順序,跟執行結果是有關系的
- private String t1 = test();
- {
- System.out.println("初始化快!");
- }
- //上面的這兩行代碼放置的順序,跟執行結果是有關系的
- private String test(){
- System.out.println("實例變量的執行過程");
- return "test";
- }
- public Test04()
- {
- System.out.println("構造方法!");
- }
- public static void main(String[] args)
- {
- Test04 t2 = new Test04();
- }
- }
實例變量的執行過程
初始化快!
構造方法!
public class NonStaticBlock {
{
System.out.println("初始化快!");
}
//下面的這兩行代碼放置的順序,跟執行結果是有關系的
private String t1 = test();
//上面的這兩行代碼放置的順序,跟執行結果是有關系的
private String test(){
System.out.println("實例變量的執行過程");
return "test";
}
public NonStaticBlock()
{
System.out.println("構造方法!");
}
public static void main(String[] args)
{
NonStaticBlock t2 = new NonStaticBlock();
}
}
初始化快!
實例變量的執行過程
構造方法!
public class NonStaticBlock {
//下面的這兩行代碼放置的順序,跟執行結果是有關系的
private String t1 = test();
//上面的這兩行代碼放置的順序,跟執行結果是有關系的
private String test(){
System.out.println("實例變量的執行過程");
return "test";
}
public NonStaticBlock()
{
System.out.println("構造方法!");
}
{
System.out.println("初始化快!");
}
public static void main(String[] args)
{
NonStaticBlock t2 = new NonStaticBlock();
}
}
實例變量的執行過程
初始化快!
構造方法!
Java中程序初始化的順序