在Java中,只有我們創建的類被JVM加載之後,我們才能夠使用。
一、類加載時機
類加載過程:
1、加載→2、驗證→3、準備→4、解析→5、初始化→6、使用→7、卸載(其中,1,2,3,5,7這幾個步驟順序是確定的,但是解析、使用則沒有確定;比如解析階段不一定必須在初始化之前或者準備之後,它可能出現在初始化之後。)
什麼情況下JVM會進行類加載操作?
1、遇到new、getstatic、putstatic、invokestatic這4跳字節碼指令時,翻譯過來就是,使用new關鍵字實例化對象的時候、讀取或者設置一個類的靜態字段(常量除外)的時候,以及調用一個類的靜態方法的時候(PS:只有對應的類纔會加載,比如,如果子類調用父類的靜態字段,那麼子類不會被加載,它的父類會被加載)。
2、使用java.lang.reflect包的方法對類進行反射調用的時候。
3、初始化一個類,發現其父類沒有被初始化時,那麼會先觸發其父類的初始化。
4、JVM啓動,包含了main函數的類會被初始化。
5、動態語言支持時(與1類似)。
二、加載
1、通過類的全限定名來獲取定義此類的二進制字節流。
2、講這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構(就是這個類的類變量、字段、方法、方法參數、修飾符等信息保存在方法區)。
3、生成一個代表這個類的Class對象,作爲方法區這個類的各種數據的訪問入口(方法區存儲的類的類變量、字段、方法、方法參數等信息通過Class對象來獲取)。
三、驗證
1、文件格式驗證。2、元數據驗證。3、字節碼驗證。4、符號引用驗證。
四、準備
正式爲類變量分配內存並設置類變量(類變量,即靜態變量,實例變量不會被分配內存)的初始值(初始值不等於代碼中設置的值,java中,每一種類型都有零值,這個時候設置初始值就是把變量設置爲零值,比如int類型爲0,long類型爲0L,boolean類型爲false等)。
五、解析
1、類或接口的解析。2、字段解析。3、類方法解析。4、接口方法解析。
六、初始化
在初始化階段,就會完成代碼設置的值的賦值。顯示執行類構造器(不是構造函數,而是比構造函數優先一級的類構造器),類構造器<clinit>方法,執行這個方法,會優先收集這個類中所有的靜態變量和靜態代碼塊,然後對它們進行賦值,這也是爲什麼靜態變量可以不用實例對象,而直接用類進行訪問的原因。然後纔是init方法,構造函數的執行,字段的賦值等。
七、類加載器
對於任意一個類,都需要由加載它的類加載器和這個類本身一桶確立其在Java虛擬機中的唯一性,每一個類加載器,都擁有一個獨立的類名稱空間。所以,比較倆個類是否相等,只有在這兩個類是由同一個類加載器加載的前提下才有意義,否則,即使這兩個類來源於同一個Class文件,被同一個JVM加載,只要加載他們的類加載器不同,這兩個類就不想等。
雙親委派模型:最頂層類加載器爲 啓動類加載器,第二層爲 擴展類加載器,第三層爲 應用程序類加載器, 第四層爲自定義類加載器。這個邏輯比較簡單,就是,當一個類需要加載的時候,它直屬的類加載器首先不會自己加載這個類,而是扔給上一層加載,依次往上,一直到頂層的啓動類加載器,只有當上一層類加載器表明,他不能加載這個類時,直屬的類加載器纔會自己加載這個類。