一、類別與類別載入器
類別載入器:實作載入階段的第一步,透過一個類別的全限定名來將這個類別的二進位位元組流載入進jvm。
類別與類別載入器:任意一個類別唯一性都是由它本身和載入它的類別載入器決定,兩個類別是否相等在它們是由同一個類別載入器載入的前提下。
jvm虛擬機器中包含兩種類別載入器:一種是啟動類別載入器(Bootstrap ClassLoader
),它是使用C 實作;另一種是其他所有用java實作的類別載入器。
從java程式角度:
1)啟動類別載入器:負責載入
2)擴充類別載入器:負責載入
3)應用程式類別載入器(系統類別載入器):它是Classloader中的getSystemClassloader()方法的回傳值。負責載入用戶類別路徑上所指定的類別庫,如果應用程式中沒有自訂類別載入器,這個就為程式中預設的類別載入器。
免費線上影片教學:java影片教學
二、雙親委派模型
除了頂層的啟動類別載入器,其餘所有類別載入器都有自己的父類別載入器。父子關係不以繼承實現,而是以組合關係來重複使用父類別載入器。
工作過程: 類別載入器接到類別載入請求–>將請求委派給父類別載入器(直到最頂層啟動類別載入器)–>父類別嘗試加載,載入失敗回饋給子類別載入器–>子類別載入器嘗試載入
雙親委派模型的好處:保證java底層API的穩定,避免載入和基本類別重名(Object)的自訂類別導致出現多個不同的重名的類別(Object),從而造成java基礎行為的混亂。
雙親委派模型原始碼:
方法加上同步鎖定保證執行緒安全,首先檢查該類別是否已載入過,如果沒有載入則呼叫父類別載入器的loadClass()方法,若父類別載入器為空說明是啟動類別載入器,則呼叫啟動類別載入器。 如果父類別載入失敗會拋出ClassNotFoundException,在呼叫自己的findClass()方法進行載入。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //同步锁 synchronized (getClassLoadingLock(name)) { // 首先检车这个类是不是已被加载 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //如果父类不为空则调用父类加载器的loadClass方法 c = parent.loadClass(name, false); } else { //没有父类则默认调用启动类加载器加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //如果父类加载器找不到这个类则抛出ClassNotFoundException } if (c == null) { // 父类加载器失败时调用自身的findClass方法加载 long t1 = System.nanoTime(); c = findClass(name); //记录 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }三、破壞雙親委派模型
#1.第一次破壞
雙親委派模型出現在JDK1. 2之後,而類別載入器和抽象類別java.lang.ClassLoader已經存在。
因此為了向前相容,JDK1.2之後在ClassLoader中新增了一個新的protected方法findClass。使用者把自己的類別載入邏輯寫在findClass方法中,而不是重寫loadClass方法,從而確保自訂的類別載入符合雙親委派模型。
2.第二次破壞模型本身有缺陷。雙親委派可以確保各個類別載入器的基礎類別的統一,這是在使用者程式碼呼叫基礎類別的情況下,如果出現基礎類別回呼使用者程式碼那就不適用了。例如涉及SPI的場景去載入所需要的SPI程式碼。
SPI機制的介紹參考其他文章。
為了解決這個問題,引入了線程上下文載入器(Thread Context ClassLoader),這個類別載入器就可以透過java.lang.Thread類別中的setContextClassLoader()方法進行設置,如果建立執行緒時未設定將會從父執行緒繼承一個,如果全域都沒有則預設就是應用程式類別載入器,利用這個載入器可以完成父類別載入器請求子類別載入器載入的動作。
###由於對程式動態性追求導致,如熱部署,熱替換等。 ######例如模組化標準OSGi R4.2中將雙親委派的樹狀結構變成了更複雜的網狀結構。 ######java文章教學推薦:###java入門教學#######以上是深入理解java之類載入器的詳細內容。更多資訊請關注PHP中文網其他相關文章!