JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成 JVM能夠直接運行的Java類型的過程。
Java類加載的過程: 加載————>驗證->準備->解析————>初始化————>使用————>卸載 (驗證->準備->解析 統稱爲連接)
加載java
將class文件字節碼內容加載到內存中,並將這些靜態數據轉換爲方法區的運行 時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲 方法區類數據訪問的入口。(這個過程須要類加載器參與)數組
連接緩存
驗證: —— 確保加載的類信息符合JVM規範,沒有安全方面的問題。 準備: —— 正式爲類變量(static變量)分配內存並設置類變量初始值的接段, 這些內存都將在方法區中進行分配。 解析: —— 虛擬機產量池內的符號引用替換爲直接引用的過程。對同一個符號引用進行屢次解析請求是很常見的,虛擬機實現可能會對第一次解析的結果進行緩存(將直接引用保存在運行時常量池中),解析動做主要針對類或接口、字段、類方法、接口方法四類符號引用進行,。安全
符號引用:用一組任何形式的字面量描述所引用的目標,符號引用與虛擬機實現的內存佈局無關,若果有了符號引用,引用目標並不必定加載到內存中。 數據結構
直接引用:直接指向目標的指針、相對偏移量或一個能間接定位到目標的句柄,符號引用與虛擬機實現的內存佈局無關,若是有了直接引用,引用目標必定加載到內存中。多線程
初始化佈局
(1)初始化階段執行類構造器()方法的過程,<clinit>()方法是由 編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊(static塊)中的語句 合併產生的。spa
(2)當一個類被初始化的時候,若是其父類沒有被初始化時,則先初始化其父類。線程
(3)虛擬機保證一個類的<clinit>()方法在多線程環境中被準確加鎖和同步,即線程安全。指針
(4)當訪問一個Java類的靜態域時,只有真正聲明這個域的類纔會被初始化。
/** * static代碼塊按順序執行, * 初始話子類時,父類沒有初始化,則先初始化父類 */ public class Demo { static { System.out.println("Demo靜態初始化塊"); } public static void main(String[] args) throws ClassNotFoundException { System.out.println("Demo的main方法"); A a = new A(); } } class A extends A_Father { static { System.out.println("A靜態初始化塊"); } public A() { System.out.println("建立A對象"); } } class A_Father { static { System.out.println("A_Father靜態初始化塊"); } } //執行結果 Demo靜態初始化塊 Demo的main方法 A_Father靜態初始化塊 A靜態初始化塊 建立A對象
主動引用
(1)new一個類對象
(2)調用類的靜態成員(除去final常量)和靜態方法
(3)使用java.lang.reflect包的方法對類進行反射調用
(4)當虛擬機啓動,java Demo,則必定初始化Demo類,其實就是先啓動main方法所在的類
(5)初始化一個類,若是它的父類沒有被初始化,則先初始化它的父類
public class Demo { //調用類的靜態方法 public static void main(String[] args) throws ClassNotFoundException { System.out.println("Demo的main方法"); //調用類的靜態成員 A a = new A(); //反射 Class.forName("leijiazai.a.A");//沒有初始化信息,即一個類只被初始化一次。 } } class A extends A_Father { private static final int i = 100; static { System.out.println("A靜態初始化塊"); } public A() { System.out.println("建立A對象"); } } //A的父類 class A_Father { static { System.out.println("A_Father靜態初始化塊"); } } //結果 Demo的main方法 A_Father靜態初始化塊 A靜態初始化塊 建立A對象
被動引用
(1)訪問一個靜態域,只有真正聲明這個域的類纔會被初始化 ———— 經過子類引用父類的靜態變量,不會致使子類被初始化
(2)經過數組定義類引用,不會觸發此類的初始化
(3)引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中)
public class Demo { public static void main(String[] args) throws ClassNotFoundException { //訪問一個A父類靜態域 System.out.println(A.j); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~"); //數組定義類引用 A[] as = new A[10];//沒有輸出 System.out.println("~~~~~~~~~~~~~~~~~~~~~~~"); //引用常量 System.out.println(A.i); } } class A extends A_Father { public static int i = 100; static { System.out.println("A靜態初始化塊"); } public A() { System.out.println("建立A對象"); } } class A_Father { public static int j = 200; static { System.out.println("A_Father靜態初始化塊"); } } //結果 A_Father靜態初始化塊 200 ~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~ A靜態初始化塊 100
http://chenzehe.iteye.com/blog/1727062