1.什麼是類加載器?java
在類加載階段,有一步是「經過類的全限定名來獲取描述此類的二進制字節流」,而所謂的類加載器就是實現這個功能的一個代碼模塊,這個動做是在Java虛擬機外部實現的,這樣作可讓應用程序本身決定如何去獲取所須要的類。學習
類加載器的做用:首先類加載器能夠實現最本質的功能即類的加載動做。同時,它還可以結合java類自己來肯定該類在Java虛擬機中的惟一性。用通俗的話來講就是:比較兩個類是否相等,只有這兩個類是由同一個類加載器加載纔有意義。不然,即便這兩個類是來源於同一個Class文件,只要加載它們的類加載器不一樣,那麼這兩個類一定不相等。spa
2.雙親委派模型繼承
從虛擬機的角度來講,只存在兩種不一樣的類加載器:一種是啓動類加載器(Bootstrap ClassLoader),該類加載器使用C++語言實現,屬於虛擬機自身的一部分。另一種就是全部其它的類加載器,這些類加載器是由Java語言實現,獨立於JVM外部,而且所有繼承自抽象類java.lang.ClassLoader。內存
從Java開發人員的角度來看,大部分Java程序通常會使用到如下三種系統提供的類加載器:開發
1)啓動類加載器(Bootstrap ClassLoader):負責加載JAVA_HOME\lib目錄中而且能被虛擬機識別的類庫到JVM內存中,若是名稱不符合的類庫即便放在lib目錄中也不會被加載。該類加載器沒法被Java程序直接引用。源碼
2)擴展類加載器(Extension ClassLoader):按《深刻理解java虛擬機》這本書上所說,該加載器主要是負責加載JAVA_HOME\lib\ext目錄中的類庫,可是貌似在JDK的安裝目錄下,沒看到該指定的目錄。該加載器能夠被開發者直接使用。虛擬機
3)應用程序類加載器(Application ClassLoader):該類加載器也稱爲系統類加載器,它負責加載用戶類路徑(Classpath)上所指定的類庫,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。it
咱們的應用程序都是由這三類加載器互相配合進行加載的,咱們也能夠加入本身定義的類加載器。這些類加載器之間的關係以下圖所示:io
如上圖所示的類加載器之間的這種層次關係,就稱爲類加載器的雙親委派模型(Parent Delegation Model)。該模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。子類加載器和父類加載器不是以繼承(Inheritance)的關係來實現,而是經過組合(Composition)關係來複用父加載器的代碼。
雙親委派模型的工做過程爲:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的加載器都是如此,所以全部的類加載請求都會傳給頂層的啓動類加載器,只有當父加載器反饋本身沒法完成該加載請求(該加載器的搜索範圍中沒有找到對應的類)時,子加載器纔會嘗試本身去加載。
使用這種模型來組織類加載器之間的關係的好處是Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。例如java.lang.Object類,不管哪一個類加載器去加載該類,最終都是由啓動類加載器進行加載,所以Object類在程序的各類類加載器環境中都是同一個類。不然的話,若是不使用該模型的話,若是用戶自定義一個java.lang.Object類且存放在classpath中,那麼系統中將會出現多個Object類,應用程序也會變得很混亂。若是咱們自定義一個rt.jar中已有類的同名Java類,會發現JVM能夠正常編譯,但該類永遠沒法被加載運行。
在rt.jar包中的java.lang.ClassLoader類中,咱們能夠查看類加載實現過程的代碼,具體源碼以下:
Java代碼
經過上面代碼能夠看出,雙親委派模型是經過loadClass()方法來實現的,根據代碼以及代碼中的註釋能夠很清楚地瞭解整個過程其實很是簡單:先檢查是否已經被加載過,若是沒有則調用父加載器的loadClass()方法,若是父加載器爲空則默認使用啓動類加載器做爲父加載器。若是父類加載器加載失敗,則先拋出ClassNotFoundException,而後再調用本身的findClass()方法進行加載。