前言
Java類的加載是動態的,它並不會一次性將所有類全部加載后再運行,而是保證程序運行的基礎類(像是基類)完全加載到jvm中,至於其他類,則在需要的時候才加載。這當然就是為了節省內存開銷。
Java虛擬機對class文件采用的是按需加載的方式,也就是說當需要使用該類時才會將它的class文件加載到內存生成class對象。而且加載某個類的class文件時,java虛擬機采用的是雙親委派模式,即把請求交由父類處理,它是一種任務委派模式。
類裝載方式,有兩種 :
- 隱式裝載, 程序在運行過程中當碰到通過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中,
- 顯式裝載, 通過class.forname()等方法,顯式加載需要的類
為什么要這么做呢?
如果沒有使用雙親委派模型,由各個類加載器自行加載的話,如果用戶自己編寫了一個稱為java.lang.Object的類,並放在程序的ClassPath中,那系統將會出現多個不同的Object類, Java類型體系中最基礎的行為就無法保證。應用程序也將會變得一片混亂。
委托機制的意義
防止內存中出現多份同樣的字節碼
比如兩個類A和類B都要加載System類:
如果不用委托而是自己加載自己的,那么類A就會加載一份System字節碼,然后類B又會加載一份System字節碼,這樣內存中就出現了兩份System字節碼。
如果使用委托機制,會遞歸的向父類查找,也就是首選用Bootstrap嘗試加載,如果找不到再向下。這里的System就能在Bootstrap中找到然后加載,如果此時類B也要加載System,也從Bootstrap開始,此時Bootstrap發現已經加載過了System那么直接返回內存中的System即可而不需要重新加載,這樣內存中就只有一份System的字節碼了。
能不能自己寫個類叫java.lang.System?
答案:通常不可以,但可以采取另類方法達到這個需求。
解釋:為了不讓我們寫System類,類加載采用委托機制,這樣可以保證父類加載器優先,父類加載器能找到的類,子加載器就沒有機會加載。而System類是Bootstrap加載器加載的,就算自己重寫,也總是使用Java系統提供的System,自己寫的System類根本沒有機會得到加載。
但是,我們可以自己定義一個類加載器來達到這個目的,為了避免雙親委托機制,這個類加載器也必須是特殊的。由於系統自帶的三個類加載器都加載特定目錄下的類,如果我們自己的類加載器放在一個特殊的目錄,那么系統的加載器就無法加載,也就是最終還是由我們自己的加載器加載。
工作原理
(1)如果一個類加載器收到了類加載請求,它並不會自已先去加載,而是把這個請求委托給父類的加載器去執行;
(2)如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸,請求最終將到達頂層的啟動類加載器;
(3)如果父類加載器可以完成類加載任務,就成功返回,若父類加載器無法完成此加載任務,子加載器才嘗試自已去加載;
