public class StaticTest { public static int k = 0; public static StaticTest t1 = new StaticTest("t1"); public static StaticTest t2 = new StaticTest("t2"); public static int i = print("i"); public static int n = 99; public int j = print("j"); { print("構造塊"); } static { print("靜態塊"); } public StaticTest(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++n; ++i; } public static int print(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++i; return ++n; } public static void main(String[] args) { StaticTest t = new StaticTest("init"); } }
1. 首先加載類,加載類過程中對於定義的靜態字段分配內存空間,也是在方法區中,並進行零初始化,即數字類型初始化為0,boolean初始化為false,引用類型初始化為null等。這也就是為什么剛開始i=0,n=0
2. 執行第一句public static int k = 0;對k進行賦值
3. 執行第二句public static StaticTest t1 = new StaticTest("t1");這是定義了一個類的靜態對象,在t1對象初始化時先執行非靜態方法或者非靜態常量,順序執行,接着運行構造參數。意思是當聲明了對象名后,我的類有了一個對象,就默認調用了類中定義的非靜態的變量 和方法,依次進行;所以這時就會調用public int j = print("j");
此時會調用print方法,其中++i,i=1=i+1; 返回值++n,n=1=n+1;j=1,k=1.
打印 1:j i=0 n=0;
然后執行非靜態塊print("構造塊");調用print函數
打印 2:構造塊 i=1 n=1
沒有了非靜態的方法,塊或常量后,之后會對t1初始化,執行類的構造函數public StaticTest(String str);
打印 3:t1 i=2 n=2
4. 執行第三句public static StaticTest t2 = new StaticTest("t2");和上面一步的分析方法一樣。
依次打印
4:j i=3 n=3
5:構造塊 i=4 n=4
6:t2 i=5 n=5
5. 執行第四句public static int i = print("i");調用print方法
打印 7:i i=6 n=6
6. 執行第五句public static int n = 99;至此靜態變量加載完畢,然后加載靜態塊和靜態方法的聲明,只有當調用靜態方法時才執行靜態方法的內容。
7. 執行第六句static{ print("靜態塊"); } 所以調用print函數
打印 8:靜態塊 i=7 n=99 由於n在上一步初始化了。
8. 執行main函數體StaticTest t = new StaticTest("init");同樣分析思路和第3步是一樣的,當聲明了對象t時,程序會先執行非靜態塊和變量以及非靜態方法的聲明。然后執行類對象的初始化。所以先執行public int j = print("j");調用了print函數
打印 9:j i=8 n=100
然后執行非靜態塊print("構造塊");調用了print函數
打印 10:構造塊 i=9 n=101
最后執行對象的初始化,調用了構造函數public StaticTest(String str);
打印 11:init i=10 n=102
程序的入口函數依然是main函數,這里的知識點在於程序時類的加載過程和java虛擬機的內存分配。程序執行的順序是,java虛擬機加載由.java編譯生成出的.class字節碼文件,將其加載至內存,然后找到了main函數,但並不是運行main函數的第一句,而是加載類的數據,當加載一個類時,JVM會根據屬性的數據類型第一時間賦默認值(一舉生成的),即數字類型初始化為0,boolean初始化為false,引用類型初始化為null等。然后再進行靜態屬性初始化,並為靜態屬性分配內存空間,靜態方法的聲明,靜態塊的加載,沒有優先級之分,按出現順序執行,靜態部分僅僅加載一次。至此為止,必要的類都已經加載完畢,對象就可以被創建了。
當對象被創建時,就會調用類的動態屬性,普通方法聲明,構造塊。最后在調用構造函數。
總的來說調用過程如下:
先加載類的靜態屬性(變量),靜態塊,靜態方法的聲明。
然后當定義了類的對象時,調用非靜態屬性(變量),構造塊,普通方法聲明。最后對象初始化調用類的構造函數。