前面一個博客文章介紹過了類從加載到可以被使用經過了加載、連接、初始化三個過程,下面對每個過程所發生的事情做詳細分析。
一、類加載階段
1.類加載方式
類的加載指的是將類的.class文件的二進制數據讀入內存中,將其放在運行時數據區的方法區內。然後在堆區創建一個java.lang.Class對象,
用來封裝類在方法區內的數據結構,該對象是由JVM在加載類時創建的。所以每個類都會對應一個Class類型的對象,通過getClass()來獲取,
並且無論生成該類的多個少對象,其Class類型的對象只有一個。Class類的構造方法是私有的,並且Class類的對象只有JVM才能創建,創建時機爲加載.class文件時。
因此Class類是整個反射的入口,因爲每個類都會在內存中對應一個描述它的Class類型的對象,使用這個對象就可以獲取到目標類所關聯的class文件中的數據結構。
類的加載有以下幾種方式(加載.class文件的方式)
Ø 從本地文件系統直接加載
Ø 通過網絡下載.class文件(java.net.URLClassLoader)
Ø 從zip、jar等歸檔文件中加載 .class文件
Ø 從專有的數據庫中提取 .class文件
Ø 將Java源文件動態編譯爲.class文件
類加載的最終產物就是位於堆區中的Class對象(注意此時並沒有被加載類的對象存在,剛剛加載類)。Class對象封裝了類在方法區中的數據結構,
並且向Java程序員提供了訪問方法區內的數據結構的接口,這些接口就是Java反射的相關類和方法。
1. 類加載器
有兩種類型的類加載器
(1).Java虛擬機自帶的類加載器,其中包括以下三種類加載器Ø 根類加載器(Bootstrap ClassLoader)
Ø 擴展類加載器(Extension ClassLoader)
Ø 系統類加載器(System ClassLoader),又稱爲應用類加載器
(2).用戶自定義的類加載器,可以定義加載的方式,加載時機以及加載過程中做一些事情。
使用java.lang.ClassLoader的子類,通過ClassLoader來實現自定義的類加載器,這個過程中可以定義類的加載方式,加載時機以及加載過程中做一些事情。
通過給定ClassLoader一個類的名稱,它會將其作爲一個文件名稱試圖去讀取該文件內容並根據內容組裝一個類的描述。每個類對象其實都包含了一個對定義它的
那個ClassLoader的一個引用,因爲任何類都是由類加載器加載的,因此通過該類就可以訪問到對應的類加載器。
通過類對象的getClass().getClassLoader()或類的class屬性的getClassLoader()就可以獲取到類所對應的類加載器,也就是加載該類的哪個類加載器。
getClassLoader()可能返回一個null,那麼就代表該類的類加載器是根類加載器(Bootstrap ClassLoader);換句話說,如果一個類是有根類加載器加載的,
那麼就無法獲取到該類加載器,此時getClassLoader()返回null,因爲根類加載器是使用C++編寫的,我們無法在程序中訪問它。例如String等類就是由根類
加載器加載的,因此String.class.getClassLoader()將返回null。
對於JDK內置的類一般都是由根類加載器加載的,因此通過它們調用getClassLoader()返回null;自定義的類一般通過
sun.misc.Launcher$AppClassLoader加載,通過輸出getClassLoader()結果即可看到。
對於JDK動態代理的InvocationHandler類的invoke方法,第一個參數是一個ClassLoader類型,就是用於動態的加載第二個參數所傳遞的類,
然後會根據所加載的類動態的創建出所加載類的對象,然後根據該對象創建出一個該對象的代理對象。
3.類加載時機
類加載器並不需要等到某個類被主動使用時才加載它。這與類的初始化不同,上面說過JVM必須在每個類或接口被Java程序首次主動使用時才初始化它們,
注意加載與初始化的卻別!
JVM規範允許類加載器在預料到某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.class文件不存在或有錯誤,此時並不會報錯,
而是類加載器必須等到在程序首次使用該類時才報告錯誤,這種錯誤類型爲LinkageError。因此如果這個類加載後一直沒有被主動使用,
那麼類加載器就一直不會報告錯誤。
類被加載後就進入了連接階段。連接就是將已經讀入到內存的類的二進制數據合併到虛擬機的運行時環境中。所謂的數據合併,因爲編譯後的每個class文件
在硬盤中都是獨立的,但是每個class之間可能存在引用關係以及方法之間存在調用關係,此時就需要根據它們之間的關係將這些class數據合併在一起放入運行時環境中。
以上介紹的是類加載、連接、初始化中的加載過程。後面的文章將介紹連接過程,包括驗證、準備、解析三個過程。最後介紹類的初始化過程。