編譯:即javac的過程,即把.java文件編譯成.class文件,即編譯成字節碼文件,同時作一些類型以及格式的檢查。java
類只有在要運行的時候纔會被加載進JVM,即編譯後只有須要到這個類的時候纔會把他加載進JVM運行,這種動態加載是依靠反射來實現的,通常來講一個class只會被加載一次,下一次就會從jvm的class緩存中獲取,不會再去文件系統中去獲取class文件了。c++
下面具體說說類的加載過程:程序員
類的裝載方式分兩種:數組
1.隱式裝載:即平時咱們經過new產生對象時,他隱式的調用類裝載器把類就加載進jvm中。沒法進行動態加載,即你new的這個 class對象必須是你程序代碼編譯的時候有的。緩存
2.顯式裝載:即利用Class.forname()來顯示的加載一個類。便可以實現動態加載,你能夠在程序運行時加載進一個編譯時並無的 類。安全
類的裝載器(負責把類加載進jvm)jvm
有三種類裝載器,爲何要有三個類加載器,一方面是分工,各自負責各自的區塊,另外一方面爲了實現委託模型,那麼遇到要加 載某一個類,三個類裝載器之間是如何工做的,具體見另外一篇《深刻理解java類加載器ClassLoader》。url
類裝載器是如何工做的,其實咱們能夠本身實現一個類加載器:.net
try {
URL url = new URL( "file:/d:/test/lib/" ); //根據路徑
URLClassLoader urlCL = new URLClassLoader( new URL[]{url});
Class c = urlCL.loadClass("TestClassA" );
TestClassA object = (TestClassA)c.newInstance();
object.method();
}catch (Exception e){
e.printStackTrace();
} 對象
從上面的代碼應該能基本知道類加載器的實現。
接下來具體講講類加載的整個過程:
當咱們想執行一個.class文件的時候,java.exe會幫助咱們找到JRE,接着找到位於JRE中的jvm.dll,這就是java虛擬機,虛擬機激活後,會先作一些初始化的動做,一旦初始化動做完成後,就會產生第一個類加載器--Bootstrap Loader(它是由c++編寫的),而後Bootstrap Loader加載Launcher.java中的ExtClassLoader加載器,並設定其parent爲null(由於其parent是Bootstrap Loader,它是由c++編寫的,沒法找到這個實例),而後Bootstrap Loader再要求加載Launcher.java中的AppClassLoader加載器,並設定其parent爲
ExtClassLoader。因此ExtClassLoader加載器和AppClassLoader加載器都是由Bootstrap Loader加載的,parent與是誰加載的並無關係。
有了類加載器以後,那麼這三個加載器之間如何協同工做,能夠看《深刻理解java類加載器ClassLoader》。至因而如何把.class文件加載進jvm的過程,從上面自定義一個類裝載器也能夠看出。
這樣加載過程就差很少了,可是一個類要可使用還要進行連接,初始化兩個過程。
加載-->連接-->初始化 其中連接還包括驗證-->準備--》解析三個過程
一、加載(即上面說的整個過程)
類的加載階段,主要是獲取定義此類的二進制字節流,並將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結 構,最後在Java堆中生成一個表明這個類的java.lang.Class對象做爲方法區這些數據的訪問入口。相對於類加載過程的其餘 階段,加載階段是開發期可控性最強的階段。咱們能夠經過定製不通的類加載器,也就是ClassLoader來控制二進制字節流的 獲取方式。
二、驗證
驗證,準備和解析其實都屬於鏈接階段,而驗證就是鏈接階段的第一步。這一階段主要是爲了確保Class文件的字節流中包含 的信息複合當前虛擬機的要求,而且不會危害虛擬機自身的安全。主要驗證過程包括:文件格式驗證,元數據驗證,字節碼驗 證以及符號引用驗證。
三、準備
準備階段正式爲類變量分配內存並設置初始值。這裏的初始值並非初始化的值,而是數據類型的默認零值。這裏提到的類 變量是被static修飾的變量,而不是實例變量。關於準備階段爲類變量設置零值的惟一例外就是當這個類變量同時也被final 修飾,那麼在編譯時,就會直接爲這個常量賦上目標值。
四、解析
解析時虛擬機將常量池中的符號引用替換爲直接引用的過程。
五、初始化
在準備階段,變量已經賦過一次系統要求的初始值,在初始化階段,則是根據程序員經過程序的主觀計劃區初始化類變量和其 他資源。Java虛擬機規範規定了有4種狀況必須當即對類進行初始化(加載,驗證,準備必須在此以前完成)
1)當使用new關鍵字實例化對象時,當讀取或者設置一個類的靜態字段(被final修飾的除外)時,以及當調用一個類的靜態 方法時(好比構造方法就是靜態方法),若是類未初始化,則需先初始化。
2)經過反射機制對類進行調用時,若是類未初始化,則需先初始化。
3)當初始化一個類時,若是其父類未初始化,先初始化父類。
4)用戶指定的執行主類(含main方法的那個類)在虛擬機啓動時會先被初始化。
除了上面這4種方式,全部引用類的方式都不會觸發初始化,稱爲被動引用。如:經過子類引用父類的靜態字段,不會致使子類 初始化;經過數組定義來引用類,不會觸發此類的初始化;引用類的靜態常量不會觸發定義常量的類的初始化,由於常量在編 譯階段已經被放到常量池中了。