一、初始化順序:父類的靜態變量-->父類的靜態代碼塊-->子類的靜態變量-->子類的靜態代碼快-->父類的非靜態變量(父類的非靜態代碼塊)-->父類的構造函數-->子類的非靜態變量(子類的非靜態代碼塊)-->子類的構造函數java
package com.test; public class Test { public static void main(String[] args) { Child c=new Child(); } } class Parent { public static PrintMessage a=new PrintMessage("父類靜態成員被初始化"); private PrintMessage b=new PrintMessage("父類非靜態成員被初始化"); static{ System.out.println("父類的靜態代碼塊被執行"); } { System.out.println("父類的非靜態代碼塊被執行"); } public Parent(){ System.out.println("父類的構造方法被執行"); } } class Child extends Parent{ public static PrintMessage a1=new PrintMessage("子類靜態成員被初始化"); private PrintMessage b1=new PrintMessage("子類非靜態成員被初始化"); static { System.out.println("子類的靜態代碼塊被執行"); } { System.out.println("子類的非靜態代碼塊被執行"); } public Child(){ System.out.println("子類的構造函數被執行"); } } class PrintMessage{ public PrintMessage(String mes){ System.out.println(mes); } }
執行結果:數據結構
父類靜態成員被初始化
父類的靜態代碼塊被執行
子類靜態成員被初始化
子類的靜態代碼塊被執行
父類非靜態成員被初始化
父類的非靜態代碼塊被執行
父類的構造方法被執行
子類非靜態成員被初始化
子類的非靜態代碼塊被執行
子類的構造函數被執行函數
二、JAVA 類的加載機制spa
Java類加載分爲5個過程,分別爲:加載,鏈接(驗證,準備,解析),初始化,使用,卸載。設計
1. 加載
加載主要是將.class文件(也能夠是zip包)經過二進制字節流讀入到JVM中。 在加載階段,JVM須要完成3件事:
1)經過classloader在classpath中獲取XXX.class文件,將其以二進制流的形式讀入內存。
2)將字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構;
3)在內存中生成一個該類的java.lang.Class對象,做爲方法區這個類的各類數據的訪問入口。code
2.鏈接對象
2.1. 驗證
主要確保加載進來的字節流符合JVM規範。驗證階段會完成如下4個階段的檢驗動做:
1)文件格式驗證
2)元數據驗證(是否符合Java語言規範)
3)字節碼驗證(肯定程序語義合法,符合邏輯)
4)符號引用驗證(確保下一步的解析能正常執行)
2.2. 準備
準備是鏈接階段的第二步,主要爲靜態變量在方法區分配內存,並設置默認初始值。
2.3. 解析
解析是鏈接階段的第三步,是虛擬機將常量池內的符號引用替換爲直接引用的過程。繼承
3. 初始化ip
初始化階段是類加載過程的最後一步,主要是根據程序中的賦值語句主動爲類變量賦值。
當有繼承關係時,先初始化父類再初始化子類,因此建立一個子類時其實內存中存在兩個對象實例。
注:若是類的繼承關係過長,單從類初始化角度考慮,這種設計不太可取。緣由我想你已經猜到了。
一般建議的類繼承關係最多不超過三層,即父-子-孫。某些特殊的應用場景中可能會加到4層,但就此打住,第4層已經有代碼設計上的弊端了。內存
4. 使用
程序之間的相互調用。
5. 卸載
即銷燬一個對象,通常狀況下中有JVM垃圾回收器完成。代碼層面的銷燬只是將引用置爲null。
經過上面的總體介紹後,再來看Singleton2.getInstance()的執行分析:
1)類的加載。運行Singleton2.getInstance(),JVM在首次並無發現Singleton類的相關信息。因此經過classloader將Singleton.class文件加載到內存中。
2)類的驗證。略
3)類的準備。將Singleton2中的靜態資源轉化到方法區。value1,value2,singleton在方法區被聲明分別初始爲0,0,null。
4)類的解析。略(將常量池內的符號引用替換爲直接引用的過程)
5)類的初始化。執行靜態屬性的賦值操做。按照順序先是value1 = 5,value2 = 3,接下來是private static Singleton2 singleton2 = new Singleton2();
這是個建立對象操做,根據 結論1 在執行Singleton2的構造方法以前,先去執行static資源和非static資源。但因爲value1,value2已經被初始化過,因此接下來執行的是非static的資源,最後是Singleton2的構造方法:value1++;value2++。
因此Singleton2結果是6和4。
以上除了搞清楚執行順序外,還有一個重點->結論2:靜態資源在類的初始化中只會執行一次。不要與第3個步驟混淆。
有了以上的這個結論,再來看Singleton.getInstance()的執行分析:
1)類的加載。將Singleton類加載到內存中。
2)類的驗證。略
3)類的準備。將Singleton2的靜態資源轉化到方法區。
4)類的解析。略(將常量池內的符號引用替換爲直接引用的過程)
5)類的初始化。執行靜態屬性的賦值操做。按照順序先是private static Singleton singleton = new Singleton(),根據 結論1 和結論2,value1和value2不會在此層執行賦值操做。因此singleton對象中的value1,value2只是在0的基礎上進行了++操做。此時singleton對象中的value1=1,value2=1。 而後, public static int value1 = 5; public static int value2 = 3; 這兩行代碼纔是真的執行了賦值操做。因此最後的結果:5和3。 若是執行的是public static int value1; public static int value2;結果又會是多少?結果: 1和1。