類加載器
從我的上一篇JVM體系結構文章的中可以看出,在 JVM 的內存模型中,類加載器是處於一個較爲重要的位置。
類加載器負責將 Java 的 .class 文件加載入 JVM 的內存區域,.class 文件的開頭都有特定的文件標識,並且將這些內容轉換成方法區中的運行時數據結構,類加載器只負責加載 .class 文件,Execution Engine 決定其是否能運行。
下圖是類加載器將 .class 文件加載到 JVM 的過程:
類加載器將 .class 文件加載後,會初始化一個類模板,該類的所有實例都會通過這個類模板創建出來。
類加載器種類
類加載器當然不止一種,從大體上,類加載器可以分爲4種。
Java 虛擬機中自帶的類加載器有:
- 啓動類加載器(Bootstrap):通過C++實現的
- 擴展類加載器(Extension):通過Java實現的
- 應用程序加載器(AppClassLoader):也稱爲系統類加載器
用戶自定義的類加載器:
- Java.lang.ClassLoader 的子類,用戶可以自定義類的加載方式
下圖爲各個類加載器的層級關係圖:
我們用代碼來測試一下,通過 Java 原生 Object 類和自定義的 App 類,查看他們的類加載器和類加載器的層級關係。
可以看出,啓動類加載器在打印的時候是 null,這是因爲啓動類加載器是通過 C++ 語言實現的,Java 沒辦法直接操作。自定義的 App 類就是應用程序加載器。
雙親委派模型
在類加載器中還需要提及的就是類加載器的雙親委派模型。
當一個類需要加載的時候,首先他不會自己去加載這個類,而是把這個類加載的請求委派給父類的類加載器加載,每一層類加載器都是這樣,直到委派給啓動類加載器,只有最頂層的類加載器無法加載這個類請求時,子類的類加載器纔會開始嘗試加載。
優點
使用雙親委派模型可以防止污染 Java 源代碼,比如我們自定義了一個 string 類,但是 java 源代碼中已經有了 string,由於我們自己自定義的 string 類加載器屬於應用程序加載器,根據雙親委派模型,會先讓啓動類加載器加載,所以會加載 jdk 中的 string 類,保證了使用不同的類加載器最終得到的是同一個對象。
我們做一個小小的測試,新建一個類叫做String,與JDK包中的String同名,實例化這個String類,看看會發生什麼??
這裏我在String類的空構造上打印了一句話,想看看在創建String對象的時候會不會調用到。
打印出來的結果是報錯,也就證明了Java檢測到我們自定義的String類和JDK中的String類同名,啓動類加載器加載時拋出錯誤。