您好,登錄后才能下訂單哦!
本篇內容介紹了“類裝載器ClassLoader是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
類裝載器就是尋找類的節碼文件并構造出類在JVM內部表示對象的組件。在Java中,類裝載器把一個類裝入JVM中,要經過以下步驟:
[1.]裝載:查找和導入Class文件; [2.]鏈接:執行校驗、準備和解析步驟,其中解析步驟是可以選擇的: [2.1]校驗:檢查載入Class文件數據的正確性; [2.2]準備:給類的靜態變量分配存儲空間; [2.3]解析:將符號引用轉成直接引用; [3.]初始化:對類的靜態變量、靜態代碼塊執行初始化工作。
類裝載工作由ClassLoader及其子類負責,ClassLoader是一個重要的Java運行時系統組件,它負責在運行時查找和裝入Class字節碼文件。JVM在運行時會產生三個ClassLoader:根裝載器、ExtClassLoader(擴展類裝載器)和AppClassLoader(系統類裝載器)。其中,根裝載器不是ClassLoader的子類,它使用C++編寫,因此我們在Java中看不到它,根裝載器負責裝載JRE的核心類庫,如JRE目標下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子類。其中ExtClassLoader負責裝載JRE擴展目錄ext中的JAR類包;AppClassLoader負責裝載Classpath路徑下的類包。
這三個類裝載器之間存在父子層級關系,即根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是AppClassLoader的父裝載器。默認情況下,使用AppClassLoader裝載應用程序的類,我們可以做一個實驗:
public class ClassLoaderTest { public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println("current loader:"+loader); System.out.println("parent loader:"+loader.getParent()); System.out.println("grandparent loader:"+loader.getParent(). getParent()); } }
運行以上代碼,在控制臺上將打出以下信息:
current loader:sun.misc.Launcher$AppClassLoader@131f71a parent loader:sun.misc.Launcher$ExtClassLoader@15601ea //①根裝載器在Java中訪問不到,所以返回null grandparent loader:null
通過以上的輸出信息,我們知道當前的ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader是根類裝載器,因為在Java中無法獲得它的句柄,所以僅返回null。
JVM裝載類時使用“全盤負責委托機制”,“全盤負責”是指當一個ClassLoader裝載一個類的時,除非顯式地使用另一個ClassLoader,該類所依賴及引用的類也由這個ClassLoader載入;“委托機制”是指先委托父裝載器尋找目標類,只有在找不到的情況下才從自己的類路徑中查找并裝載目標類。這一點是從安全角度考慮的,試想如果有人編寫了一個惡意的基礎類(如java.lang.String)并裝載到JVM中將會引起多么可怕的后果。但是由于有了“全盤負責委托機制”,java.lang.String永遠是由根裝載器來裝載的,這樣就避免了上述事件的發生。
在Java中,ClassLoader是一個抽象類,位于java.lang包中。下面對該類的一些重要接口方法進行介紹:
Class loadClass(String name)
name參數指定類裝載器需要裝載類的名字,必須使用全限定類名,如com.baobaotao. beans.Car。該方法有一個重載方法loadClass(String name ,boolean resolve),resolve參數告訴類裝載器是否需要解析該類。在初始化類之前,應考慮進行類解析的工作,但并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要進行解析。
Class defineClass(String name, byte[] b, int off, int len)
將類文件的字節數組轉換成JVM內部的java.lang.Class對象。字節數組可以從本地文件系統、遠程網絡獲取。name為字節數組對應的全限定類名。
Class findSystemClass(String name)
從本地文件系統載入Class文件,如果本地文件系統不存在該Class文件,將拋出ClassNotFoundException異常。該方法是JVM默認使用的裝載機制。
Class findLoadedClass(String name)
調用該方法來查看ClassLoader是否已裝入某個類。如果已裝入,那么返回java.lang.Class對象,否則返回null。如果強行裝載已存在的類,將會拋出鏈接錯誤。
ClassLoader getParent()
獲取類裝載器的父裝載器,除根裝載器外,所有的類裝載器都有且僅有一個父裝載器,ExtClassLoader的父裝載器是根裝載器,因為根裝載器非Java編寫,所以無法獲得,將返回null。
除JVM默認的三個ClassLoader以外,可以編寫自己的第三方類裝載器,以實現一些特殊的需求。類文件被裝載并解析后,在JVM內將擁有一個對應的java.lang.Class類描述對象,該類的實例都擁有指向這個類描述對象的引用,而類描述對象又擁有指向關聯ClassLoader的引用,如圖所示。
每一個類在JVM中都擁有一個對應的java.lang.Class對象,它提供了類結構信息的描述。數組、枚舉、注解以及基本Java類型(如int、double等),甚至void都擁有對應的Class對象。Class沒有public的構造方法。Class對象是在裝載類時由JVM通過調用類裝載器中的defineClass()方法自動構造的。
類什么時候才被初始化
創建類的實例,也就是new一個對象
訪問某個類或接口的靜態變量,或者對該靜態變量賦值
調用類的靜態方法
反射(Class.forName("com.lyj.load"))
初始化一個類的子類(會首先初始化子類的父類)
JVM啟動時標明的啟動類,即文件名和類名相同的那個類
只有這6中情況才會導致類的類的初始化。
類的初始化步驟:
如果這個類還沒有被加載和鏈接,那先進行加載和鏈接
假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用于接口)
加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。
“類裝載器ClassLoader是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。