概念
????????類加載器:只參與加載過程中的字節碼獲取并加載到內存中的部分;java虛擬機提供給應用程序去實現獲取類和接口字節碼數據的一種技術,也就是說java虛擬機是允許程序員寫代碼去獲取字節碼信息
????????類加載是加載的第一步,主要有以下三個步驟:
? ? ? ? ????????(1)通過全類名獲取定義此類的二進制字節流;
? ? ? ? ????????(2)將字節碼文件的靜態存儲結構轉換成方法區運行時的數據結構;
? ? ????????? ? (3)在內存中生成一個該類的class對象,作為方法區的訪問入口;
????????類加載器:
? ? ? ????????? (1)類加載器是負責加載類的一個對象,用于類加載中加載這一步;
? ? ? ????????? (2)每一個java類都有一個引用指向加載他的ClassLoader;
? ? ? ????????? (3)數組類不是通過ClassLoader創建的(數組類沒有對應的二進制字節流),是有jvm直接生成的
其實除了加載類之外,類加載器還可以加載 Java 應用所需的資源如文本、圖像、配置文件、視頻等等文件資源。本文只討論其核心功能:加載類。
類加載器的分類
? ? ? ? (1)jvm底層源碼實現:實現語言和虛擬機底層語言一致,比如Hotspot使用C++。主要目的是保證Java程序運行中基礎類被正確地加載,比如java.lang.String,Java虛擬機需要確保其可靠性。
? ? ? ? (2)java代碼實現:jdk默認提供的類加載器,或者是程序員按照需求定制,所有java實現的類加載類都需要繼承classloader這個抽象類。
jdk8以前:
啟動類加載器
-
啟動類加載器(Bootstrap ClassLoader)是由Hotspot虛擬機提供的、使用C++編寫的類加載器。
-
默認加載Java安裝目錄/jre/lib下的類文件,比如rt.jar,tools.jar,resources.jar等。
/*** 啟動程序類加載器案例*/
public class BootstrapClassLoaderDemo {public static void main(String[] args) throws IOException {ClassLoader classLoader = String.class.getClassLoader();System.out.println(classLoader);System.in.read();}
}
? ? ? ?上面的代碼打印出來的類加載器為null,因為啟動類加載器在jdk8中是由c++來編寫的,在java代碼中獲取不安全,所以返回了null。
如果用戶想擴展一些比較基礎的jar包,讓啟動類加載器加載,有兩種途徑:
-
放入jre/lib下進行擴展。不推薦,盡可能不要去更改JDK安裝目錄中的內容,會出現即時放進去由于文件名不匹配的問題也不會正常地被加載。
-
使用參數進行擴展。推薦,使用-Xbootclasspath/a:jar包目錄/jar包名 進行擴展,參數中的/a代表新增。
擴展類加載器和應用程序加載器
(1)擴展類加載器和應用程序加載器都是由jdk提供,使用java代碼編寫;
(2)它們的源碼都位于sun.misc.Launcher中,是一個靜態內部類。繼承自URLClassLoader(具備通過目錄或者指定jar包將字節碼文件加載到內存中)
擴展類加載器
? ? ? ?擴展類加載器(Extension Class Loader)是JDK中提供的、使用Java編寫的類加載器。默認加載Java安裝目錄/jre/lib/ext下的類文件。
/*** 擴展類加載器*/
public class ExtClassLoaderDemo {public static void main(String[] args) throws IOException {ClassLoader classLoader = ScriptEnvironment.class.getClassLoader();System.out.println(classLoader);}
}
通過擴展類加載器去加載用戶jar包:
-
放入/jre/lib/ext下進行擴展。不推薦,盡可能不要去更改JDK安裝目錄中的內容。
-
使用參數進行擴展使用參數進行擴展。推薦,使用-Djava.ext.dirs=jar包目錄 進行擴展,這種方式會覆蓋掉原始目錄,可以用;(windows):(macos/linux)追加上原始目錄
如下圖中:
????????使用引號
將整個地址包裹起來,這樣路徑中即便是有空格也不需要額外處理。路徑中要包含原來ext文件夾,同時在最后加上擴展的路徑。
應用程序加載器
/*** 應用程序類加載器案例*/
public class AppClassLoaderDemo {public static void main(String[] args) throws IOException, InterruptedException {//當前項目中創建的Student類Student student = new Student();ClassLoader classLoader = Student.class.getClassLoader();System.out.println(classLoader);//maven依賴中包含的類ClassLoader classLoader1 = FileUtils.class.getClassLoader();System.out.println(classLoader1);Thread.sleep(1000);System.in.read();}
}
雙親委派機制
? ? ? ? 概念:
????????????????向上檢查是加載過這個類,向下加載;在類加載的過程中,每個類加載器都會先檢查是
????????否已經加載了該類,如果已經加載則直接返回,否則會將加載請求委派給父類加載器。
例子:
????????B類在擴展類加載器加載路徑中,同樣應用程序類加載器接到了加載任務,按照案例1中的方式一層一層向上查找,發現都沒有加載過。那么啟動類加載器會首先嘗試加載。它發現這類不在它的加載目錄中,向下傳遞給擴展類加載器。
如果第二次再接收到加載任務,同樣地向上查找。擴展類加載器發現已經加載過,就可以返回了。
????????作用:
????????????????1.保證類加載的安全性。通過雙親委派機制避免惡意代碼替換JDK中的核心類庫,比如
????????java.lang.String,確保核心類庫的完整性和安全性。
????????????????2.避免重復加載。雙親委派機制可以避免同一個類被多次加載。
打破雙親委派機制:
(1)自定義類加載器;
(2)線程上下文加載器;
(3)osgi框架的類加載器;