Java中的ClassLoader類加載器使用詳解
1. CLASSLOADER是什么
ClassLoader,類加載器。用于將CLASS文件動(dòng)態(tài)加載到JVM中去,是所有類加載器的基類(Bootstrap ClassLoader不繼承自ClassLoader),所有繼承自抽象的ClassLoader的加載器,都會(huì)優(yōu)先判斷是否被父類加載器加載過,防止多次加載。
官網(wǎng)的JVM:https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf
(1)定義:
把“類加載階段”中的「通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流」這個(gè)動(dòng)作放到JAVA虛擬機(jī)外部去實(shí)現(xiàn),以便讓應(yīng)用程序自己決定如何去獲取所需要的類。
實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊稱為「類加載器」。
(2)作用:
CLASSLOADER的作用就是將CLASS文件讀入內(nèi)存中,并為之生成對(duì)應(yīng)的java.lang.Class對(duì)象。
2. 三個(gè)默認(rèn)CLASSLOADER
①. BootStrap ClassLoader : 啟動(dòng)類加載器,在JVM啟動(dòng)時(shí)創(chuàng)建,用于加載JAVA核心類庫(kù)。它是JVM的一部分,主要加載JVM自身工作所需要的類。
②. Extension ClassLoader : 擴(kuò)展類加載器,加載目錄%JRE_HOME%\lib\ext目錄下的JAR包和CLASS文件。
③. Application ClassLoader : 應(yīng)用程序類加載器,加載應(yīng)用程序CLASSPATH目錄下的所有JAR包和CLASS文件。
3. 默認(rèn)CLASSLOADER的關(guān)系
(1) 加載關(guān)系
①. ExtentionClassLoader的父加載器是BootstrapClassLoader。 ②. AppclassLoader的父加載器是ExtentionClassLoader。
(2) 繼承關(guān)系
①. ExtentionClassLoader對(duì)應(yīng)的是ExtClassLoader.java,是在sun.misc.Launcher類中作為一個(gè)內(nèi)部類的,父類是java.net.URLClassLoader;
②. AppclassLoader對(duì)應(yīng)的是AppClassLoader.java,也是在sun.misc.Launcher類中作為一個(gè)內(nèi)部類的,父類也是java.net.URLClassLoader;
③. BootstrapClassLoader是C++編寫的,沒有對(duì)應(yīng)的java類,所以也成不了父類。
(3) 檢查順序和加載順序
AppClassLoader和ExtClassLoader是Launcher的靜態(tài)內(nèi)部類,在程序啟動(dòng)時(shí)JVM會(huì)創(chuàng)建Launcher對(duì)象,Launcher構(gòu)造器會(huì)同時(shí)會(huì)創(chuàng)建擴(kuò)展類加載器和應(yīng)用類加載器。
①. 加載過程中會(huì)先檢查類是否被已加載,檢查順序是自底向上,從User ClassLoader到BootStrap ClassLoader逐層檢查,只要某個(gè)CLASSLOADER已加載過此類,就視為此類已被加載,保證此類在所有CLASSLOADER中只被加載一次;加載順序是自頂向下,從BootStrap ClassLoader到User ClassLoader逐層嘗試加載此類;
②. 在加載類時(shí),每個(gè)類加載器都會(huì)將加載任務(wù)上交給其父類,如果其父類找不到,再由自己去加載;
③. Bootstrap Loader(啟動(dòng)類加載器)是最頂級(jí)的類加載器了,其父加載器為NULL。
4. 自定義CLASSLOADER
如果我們自定義一個(gè)加載器,一般繼承自java.lang.ClassLoader類,或者繼承他的子類,比如:java.net.URLClassLoader,此時(shí)默認(rèn)的父加載器是AppClassLoader。具體步驟如下:
①. 編寫一個(gè)繼承自 java.lang.ClassLoader 或者 java.net.URLClassLoader 的類;
②. 重寫findClass()方法或者重寫loadClass()方法;
③. 在findClass()中使用defineClass()方法,這個(gè)方法是父類ClassLoader中的方法。
// 定義一個(gè)JAVA類 public class Hello { public String say() { System.out.println("HELLO WORLD!"); return "OK"; } } // 自定義類加載器 public class MyClassLoader extends ClassLoader { private String path; MyClassLoader(String path) { this.path = path; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { File file = new File(this.path.concat(name).concat(".class")); FileInputStream in = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] bytes = new byte[1024]; int length = -1; while ((length = in.read(bytes)) != -1) { out.write(bytes, 0, length); } return defineClass(name, bytes, 0, bytes.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return super.findClass(name); } } // 調(diào)用測(cè)試 public class T { public static void main(String[] args) throws Exception { String path = Hello.class.getClassLoader().getResource("org/apache/atlas/util/").getPath(); MyClassLoader my = new MyClassLoader(path); // 注意需要帶上包路徑 Class<?> cls = my.loadClass("org.apache.atlas.util.Hello"); Object hello = cls.newInstance(); Method method = cls.getMethod("say", null); method.invoke(hello); } }
5. 使用自定義的CLASSLOADER是否可以熱加載CLASS?
①. JAVA目前沒有專門的API,來卸載JVM中已加載的類;
②. 要卸載JVM中的類,需要該類的對(duì)象都被回收,加載該類的ClassLoader也被回收,使用該類的線程結(jié)束等條件;
③. 但是,在JAVA中不同的ClassLoader實(shí)例可以加載同一個(gè)類,即使CLASS文件是同一個(gè)也可以被加載。但是同一個(gè)ClassLoader實(shí)例不能重復(fù)加載同一個(gè)類;
④. 綜上, 雖然可以熱加載CLASS,但是在不停服務(wù)的情況下熱替換CLASS不是很建議,雖然可以通過新建CLASSLOADER實(shí)例的方法來改變新加載的CLASS的內(nèi)容,但之前CLASSLOADER加載的類和對(duì)象并不會(huì)被修改,什么時(shí)候能被GC回收不可控。
到此這篇關(guān)于Java中的ClassLoader類加載器使用詳解的文章就介紹到這了,更多相關(guān)ClassLoader的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot @PathVariable使用時(shí)遇到的問題及解決
這篇文章主要介紹了SpringBoot @PathVariable使用時(shí)遇到的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java函數(shù)式編程(三):列表的轉(zhuǎn)化
這篇文章主要介紹了Java函數(shù)式編程(二):列表的轉(zhuǎn)化,lambda表達(dá)式不僅能幫助我們遍歷集合,并且可以進(jìn)行集合的轉(zhuǎn)化,需要的朋友可以參考下2014-09-09Spring通過<import>標(biāo)簽導(dǎo)入外部配置文件
之前文章里我們講到Spring加載Xml配置文件的細(xì)節(jié),那么加載完了我們肯定要解析這個(gè)配置文件中定義的元素。這篇我們首先來分析下Spring是如何通過標(biāo)簽導(dǎo)入外部配置文件的。2021-06-06SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢
這篇文章主要為大家詳細(xì)介紹了SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06解決rror updating database.Cause:java.sql.SQLSyntaxE
這篇文章主要介紹了解決rror updating database.Cause:java.sql.SQLSyntaxErrorException問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05用Java實(shí)現(xiàn)春聯(lián)?支持自定義字體顏色
大家好,本篇文章主要講的是用Java編寫春聯(lián)?支持自定義字體顏色,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01