摘要:當(dāng)一個(gè)文件是通過(guò)網(wǎng)絡(luò)傳輸并且可能會(huì)進(jìn)行相應(yīng)的加密操作時(shí),需要先對(duì)文件進(jìn)行相應(yīng)的解密后再加載到內(nèi)存中,這種情況下也需要編寫(xiě)自定義的并實(shí)現(xiàn)相應(yīng)的邏輯
Java虛擬機(jī)中的類加載有三大步驟:,鏈接,初始化.其中加載是指查找字節(jié)流(也就是由Java編譯器生成的class文件)并據(jù)此創(chuàng)建類的過(guò)程,這中間我們需要借助類加載器來(lái)查找字節(jié)流.
Java虛擬機(jī)默認(rèn)類加載器Java虛擬機(jī)提供了3種類加載器,啟動(dòng)(Bootstrap)類加載器、擴(kuò)展(Extension)類加載器、應(yīng)用(Application)類加載器.除了啟動(dòng)類加載器外,其他的類加載器都是java.lang.ClassLoader的子類.啟動(dòng)類加載器由C++語(yǔ)言實(shí)現(xiàn),沒(méi)有對(duì)應(yīng)的Java對(duì)象,它負(fù)責(zé)將
注意這里面的父子類加載器并不是繼承的關(guān)系,只是ClassLoader類中的parent屬性.我們來(lái)看Launcher類中創(chuàng)建擴(kuò)展類加載器的代碼:
public ExtClassLoader(File[] var1) throws IOException { super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this); }
這里設(shè)置了其父加載器為null.
雙親委派機(jī)制Java虛擬機(jī)在加載類時(shí)默認(rèn)采用的是雙親委派機(jī)制,即當(dāng)一個(gè)類加載器接收到加載請(qǐng)求時(shí),會(huì)將請(qǐng)求轉(zhuǎn)發(fā)到父類加載器,如果父類加載器在路徑下沒(méi)有找到該類,才會(huì)交給子類加載器去加載.我們來(lái)看ClassLoader中l(wèi)aodClass方法:
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先判斷類是否已加載過(guò),加載過(guò)就直接返回 Class> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //有父類加載器,調(diào)用父加載器的loadClass c = parent.loadClass(name, false); } else { //調(diào)用Bootstrap Classloader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { long t1 = System.nanoTime(); //到自己指定類加載路徑下查找是否有class字節(jié)碼 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
通過(guò)這種層級(jí)我們可以避免類的重復(fù)加載,當(dāng)父親已經(jīng)加載了該類時(shí),就沒(méi)有必要子類加載器再加載一次。其次也考慮到安全因素,比如我們自己寫(xiě)一個(gè)java.lang.String的類,通過(guò)雙親委派機(jī)制傳遞到啟動(dòng)類加載器,而啟動(dòng)類加載器在核心Java API發(fā)現(xiàn)這個(gè)名字的類,發(fā)現(xiàn)該類已被加載,并不會(huì)重新加載我們新寫(xiě)的java.lang.String,而直接返回已加載過(guò)的String.class,這樣保證生成的對(duì)象是同一種類型.
自定義類加載器除了jvm自身提供的類加載器,我們還可以自定義類加載器,我們先寫(xiě)一個(gè)Person類
public class Person { private int age; private String name; //省略getter/setter方法 }
我們先看他是由哪個(gè)類加載器加載的.
public class TestJava { public static void main(String[] args) throws Exception { Person person = new Person(); System.out.println("person是由" + person.getClass().getClassLoader() + "加載的"); } }
運(yùn)行結(jié)果如下:
我們把Person.class放置在其他目錄下
再運(yùn)行會(huì)發(fā)生什么,在上面的loadClass方法中其實(shí)已經(jīng)有了答案,會(huì)拋出ClassNotFoundException,因?yàn)樵谥付窂较虏檎也坏阶止?jié)碼.
我們現(xiàn)在寫(xiě)一個(gè)自定義的類加載器,讓他能夠去加載person類,很簡(jiǎn)單,我們只需要繼承ClassLoader并重寫(xiě)findClass方法,這里面寫(xiě)查找字節(jié)碼的邏輯.
public class PersonCustomClassLoader extends ClassLoader { private String classPath; public PersonCustomClassLoader(String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll(".", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } protected Class> findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); return defineClass(name, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } }
我們來(lái)測(cè)試一下:
public class TestJava { public static void main(String[] args) throws Exception { PersonCustomClassLoader classLoader = new PersonCustomClassLoader("/home/shenxinjian"); Class> pClass = classLoader.loadClass("me.shenxinjian.algorithm.Person"); System.out.println("person是由" + pClass.getClassLoader() + "類加載器加載的"); } }
測(cè)試結(jié)果如下:
當(dāng)class文件不在classPath路徑下,如上面那種情況,默認(rèn)系統(tǒng)類加載器無(wú)法找到該class文件,在這種情況下我們需要實(shí)現(xiàn)一個(gè)自定義的classLoader來(lái)加載特定路徑下的class文件來(lái)生成class對(duì)象。
當(dāng)一個(gè)class文件是通過(guò)網(wǎng)絡(luò)傳輸并且可能會(huì)進(jìn)行相應(yīng)的加密操作時(shí),需要先對(duì)class文件進(jìn)行相應(yīng)的解密后再加載到JVM內(nèi)存中,這種情況下也需要編寫(xiě)自定義的ClassLoader并實(shí)現(xiàn)相應(yīng)的邏輯
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73405.html
摘要:它負(fù)責(zé)將的字節(jié)碼形式轉(zhuǎn)換成內(nèi)存形式的對(duì)象。先使用工具對(duì)字節(jié)碼文件進(jìn)行加密,運(yùn)行時(shí)使用定制的先解密文件內(nèi)容再加載這些解密后的字節(jié)碼。的方法是需要子類來(lái)覆蓋的,不同的加載器將使用不同的邏輯來(lái)獲取目標(biāo)類的字節(jié)碼。 ClassLoader 詳解 ClassLoader 做什么的? 延遲加載 各司其職 ClassLoader 傳遞性 雙親委派 Class.forName 自定義加載器 Clas...
摘要:加載器種類啟動(dòng)類加載器在中用來(lái)加載自身需要的類,實(shí)現(xiàn),用來(lái)加載。那么就能保證的類會(huì)被優(yōu)先加載,限制了使用者對(duì)系統(tǒng)的影響。這種方式下就完成類加載器的雙親委派機(jī)制此處會(huì)將作為參數(shù)傳入進(jìn)去實(shí)際上是調(diào)用了方法 Class 文件的裝載流程 (類加載過(guò)程) 加載 -> 連接 (驗(yàn)證 -> 準(zhǔn)備 -> 解析) -> 初始化 -> 使用 -> 卸載 加載 加載階段,jvm 會(huì)通過(guò)類名獲取到此類的字節(jié)碼...
摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...
摘要:反射機(jī)制的應(yīng)用實(shí)例在泛型為的中存放一個(gè)類型的對(duì)象。工廠模式可以參考現(xiàn)在我們利用反射機(jī)制實(shí)現(xiàn)工廠模式,可以在不修改工廠類的情況下添加任意多個(gè)子類。 學(xué)習(xí)交流群:669823128java 反射 定義 功能 示例概要:Java反射機(jī)制詳解| |目錄 1反射機(jī)制是什么 2反射機(jī)制能做什么 3反射機(jī)制的相關(guān)API 通過(guò)一個(gè)對(duì)象獲得完整的包名和類名 實(shí)例化Class類對(duì)象 獲取一個(gè)對(duì)象的父類與...
摘要:再附一部分架構(gòu)面試視頻講解本文已被開(kāi)源項(xiàng)目學(xué)習(xí)筆記總結(jié)移動(dòng)架構(gòu)視頻大廠面試真題項(xiàng)目實(shí)戰(zhàn)源碼收錄 Java反射(一)Java反射(二)Java反射(三)Java注解Java IO(一)Java IO(二)RandomAccessFileJava NIOJava異常詳解Java抽象類和接口的區(qū)別Java深拷貝和淺拷...
閱讀 654·2021-11-25 09:43
閱讀 1671·2021-11-18 10:02
閱讀 1044·2021-10-15 09:39
閱讀 1894·2021-10-12 10:18
閱讀 2126·2021-09-22 15:43
閱讀 777·2021-09-22 15:10
閱讀 2091·2019-08-30 15:53
閱讀 993·2019-08-30 13:00