類加載和初始化只進行一次
1,加載(需要類加載器的支持):這個階段會在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的入口。注意這里不一定非得要從一個Class文件獲取,這里既可以從ZIP包中讀取(比如從jar包和war包中讀取),也可以在運行時計算生成(動態代理),也可以由其它文件生成(比如將JSP文件轉換成對應的Class類)。
2,驗證:這一階段的主要目的是為了確保Class文件的字節流中包含的信息是否符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。
3,准備(都是默認值):准備階段是正式為類變量分配內存並設置類變量初始值(通常情況下是數據類型的零值)的階段,這些變量所使用的內存都將在方法區中進行分配。這時候進行內存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化的時候隨着對象一起分配在Java堆中。
4,解析:解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程。
5.初始化:執行類構造器clinit()方法的過程,這個方法是由編譯器自動收集類中所有的類變量的賦值動作和靜態語句塊中的語句合並而成
類初始化階段是類加載過程的最后一步,到了這個階段才真正開始執行類中定義的Java程序代碼(或者說是字節碼)。在准備階段,變量已經賦過一次系統要求的初始值,而在初始化階段,則根據程序員通過程序制定的主觀計划去初始化類變量和其他資源。
注意以下幾點:
1.編譯器收集的順序是由語句在源文件中出現的順序決定的,
靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,而定義在它之后的變量,在前面的靜態語句塊可以賦值,但不能訪問,代碼解釋如下:
public class Test{ static{ i=0;//給變量賦值可以編譯通過 sout(i);//編譯器會提示非法向前引用 } static int i = 1; }
2.初始化方法執行的順序,虛擬機會保證在子類的初始化方法執行之前,父類的初始化方法已經執行完畢,
3.虛擬機會保證一個類的clinit()方法在多線程環境中被正確加鎖和同步
4.當訪問一個java類的靜態域時,只有真正聲明這個域的類才會被初始化
類的主動引用(會發生初始化)
- new一個類的對象
- 調用類的靜態成員(除了final常量)和靜態方法
- 反射調用時
- 啟動main方法所在的類
- 虛擬機會保證在子類的初始化方法執行之前,父類的初始化方法已經執行完畢,
類的被動引用(不會發生初始化)
- 引用常量不會引發類的初始化:常量在編譯階段就存入調用類的常量池中
- 通過數組進行類引用,不會觸發初始化
- 訪問一個靜態域(靜態變量)時,只有真正聲明這個域的類才會被初始化(子類)
類加載器:
1.作用:將字節碼文件加載到內存中,將靜態數據轉成方法區運行時數據結構,在堆中生成一個Class類對象,作為方法區類數據的方法的訪問入口
層次結構(樹狀結構)
2.ClassLoader相關方法:
3,類加載器的代理模式
雙親委托機制