一、程序使用java類的運行順序
當程序主動使用某個類的時候,若該類還未被加載至內存中,系統會通過 加載,連接,初始化 三個步驟對類進行初始化,有事也把這三個步驟稱為類加載或者類的初始化。
1 類的加載
將被編譯的.java而成為.class字節碼讀入JVM內存并為之創建一個java.lang.Class對象,也就是說當程序中使用任何類的時候系統都會為之建立一個java.lang.Class對象。類的加載由類加載器完成,類加載器通常有JVM提供,我們稱JVM提供的類加載器為系統類加載器。當然實際的情況可能更加復雜比如Java字節代碼可能是通過工具動態生成的,也可能是通過網絡下載的。
2 類的連接
當生成一個對應的.class對象后,接著會進入連接階段,會將類的二進制數據合并到JRE中,該階段又認為驗證,準備,解析三個階段。
3 類的初始化
由JVM負責對類進行初始化,主要就是對靜態屬性進行初始化。(1)聲明靜態屬性時指定初始值 (2)使用靜態初始化塊為靜態屬性指定初始值。
本文主要講解第一個步驟——類的加載。
二、類的加載的基本概念
基本上所有的類加載器都是java.lang.ClassLoader類的一個實例。 java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的字節代碼,然后從這些字節代碼中定義出一個Java類,即java.lang.Class類的一個實例。 除此之外ClassLoader還負責加載Java應用所需的資源,如圖像文件和配置文件等。不過本文只討論其加載類的功能。為了完成加載類的這個職責,ClassLoader提供了一系列的方法。JDK中幾個比較重要的方法:
getParent()
返回委托的父類加載器
loadClass(String name)
加載名稱為name的類,返回的結果是java.lang.Class類的實例
findClass(String name)
查找名稱為name的類,返回的結果是java.lang.Class類的實例
findLoadedClass(String name)
查找名稱為 name的已經被加載過的類,返回的結果是 java.lang.Class類的實例
defineClass(String name, byte[] b, int off, int len)
把字節數組b中的內容轉換成Java類,返回的結果是java.lang.Class類的實例。此方法被聲明為final
resolveClass(Class<?> c)
鏈接指定的Java類
上述name是
二進制名
,按照《Java Language Specification》的定義,任何作為String類型參數傳遞給ClassLoader中方法的類名稱都必須是一個二進制名稱。 有效類名稱的示例
包括:
"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"
三、類加載器的分類
Java 中的類加載器大致可以分成兩類,一類是系統(JVM)提供的,一類是由Java應用開發人員編寫的。
系統提供的類加載器主要有下面三個:
引導類加載器(bootstrap class loader)
用來加載 Java 的核心庫,是用原生代碼(C++)來實現的,并不繼承自java.lang.ClassLoader。
擴展類加載器(extensions class loader)
用來加載Java的擴展庫。Java虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。
系統類加載器(system class loader)
根據Java應用的類路徑(CLASSPATH)來加載Java類。一般來說Java應用的類都是由它來完成加載的。可以通過 ClassLoader.getSystemClassLoader()來獲取它。
除了系統提供的類加載器外開發人員可以通過繼承java.lang.ClassLoader類的方式實現自己的類加載器,以滿足一些特殊的需求。
除了引導類加載器外,所有的類加載器都有一個父類加載器。 通過getParent()方法可以得到。對于系統提供的類加載器來說,系統類加載器的父類加載器是擴展類加載器,而擴展類加載器的父類加載器是引導類加載器;對于開發人員編寫的類加載器來說,其父類加載器是加載此類加載器Java類的類加載器。因為類加載器Java類如同其它的Java類一樣,也要由類加載器來加載的。一般來說開發人員編寫的類加載器的父類加載器是系統類加載器。類加載器通過這種方式組織起來,形成樹狀結構。樹的根節點就是引導類加載器。 看一張傳智播客的圖
看一段測試代碼
public class Test { public static void main(String[] args) { // sun.misc.Launcher$AppClassLoader System.out.println(Test.class.getClassLoader().getClass().getName()); // null // 類加載器本身也是java類,也需要被加載,但是第一個加載器如果是java類的話,被誰來加載呢? // 答案BootStrap是第一個類加載器,是C++代碼寫成的二進制代碼,這里通過java獲取不了 System.out.println(System.class.getClassLoader()); // sun.misc.Launcher$AppClassLoader——>sun.misc.Launcher$ExtClassLoader——>null ClassLoader loader = Test.class.getClassLoader(); while (null != loader) { System.out.println(loader.getClass().getName()); loader = loader.getParent(); } System.out.println(loader); } }
四、類加載器的父類委托機制
Java的類加載器自從JDK1.2開始便引入了一條機制,叫做父類委托機制。
一個類需要被加載的時候,JVM先會調用他的父類加載器進行加載。如果父類加載器加載不了,再使用其子類進行加載。 當然這類所說的父類加載器,不一定他們之間是繼承的關系,有可能僅僅是包裝的關系。不能片面理解。
當Java虛擬機要加載類時,如果上一級加載器可以加載則交由上一級加載,如用戶編寫一個java.lang.System類,交給AppClassLoader加載—>ExtClassLoader加載—>BootStrap加
載,而rt.jar這個包中本來就有一個System類,所以用戶編寫的System類不起作用。如果BootStrap加載不到,則將由ExtClassLoader加載—>AppClassLoader加載,最后還加載不了就返回ClassNotFoundException異常,不會交給發起請求的加載器的子加載器。
Java之所以出現這條機制,因為是處于安全性考慮。害怕用戶自己定義class文件然后自己寫一個類加載器來加載原本應該是JVM自己加載的類。這樣會是JVM虛擬機混亂或者說會影響到用戶的安全。
參考地址
http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html#major5
http://blog.csdn.net/a352193394/article/details/7343385
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
