任意一個類,都由加載它的類加載器和這個類自己一同確立其在 Java 虛擬機中的惟一性,每個類加載器,都有一個獨立的類名稱空間。java
所以,比較兩個類是否「相等」,只有在這兩個類是由同一個類加載器加載的前提下才有意義,不然,即便這兩個類來源於同一個 Class 文件,被同一個虛擬機加載,只要加載它們的類加載器不一樣,那麼這兩個類就一定不相等。spa
這裏的「相等」,包括表明類的 Class 對象的 equals() 方法、isInstance() 方法的返回結果,也包括使用 instanceof 關鍵字作對象所屬關係斷定等狀況。code
系統提供了 3 種類加載器:對象
負責將存放在 <JAVA_HOME>\lib
目錄中的,而且能被虛擬機識別的(僅按照文件名識別,如 rt.jar,名字不符合的類庫即便放在 lib 目錄中也不會被加載)類庫加載到虛擬機內存中。blog
負責加載 <JAVA_HOME>\lib\ext
目錄中的全部類庫,開發者能夠直接使用擴展類加載器。繼承
因爲這個類加載器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此通常也稱它爲「系統類加載器」。它負責加載用戶類路徑(classpath)上所指定的類庫,開發者能夠直接使用這個類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。內存
固然,若是有必要,還能夠加入本身定義的類加載器。開發
雙親委派模型是描述類加載器之間的層次關係。它要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。(父子關係通常不會以繼承的關係實現,而是以組合關係來複用父加載器的代碼)rem
若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求(找不到所需的類)時,子加載器纔會嘗試本身去加載。get
在 java.lang.ClassLoader 中的 loadClass() 方法中實現該過程。
像 java.lang.Object 這些存放在 rt.jar 中的類,不管使用哪一個類加載器加載,最終都會委派給最頂端的啓動類加載器加載,從而使得不一樣加載器加載的 Object 類都是同一個。
相反,若是沒有使用雙親委派模型,由各個類加載器自行去加載的話,若是用戶本身編寫了一個稱爲 java.lang.Object 的類,並放在 classpath 下,那麼系統將會出現多個不一樣的 Object 類,Java 類型體系中最基礎的行爲也就沒法保證。