日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

了解和扩展Java ClassLoader

發(fā)布時間:2023/12/3 java 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 了解和扩展Java ClassLoader 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Java ClassLoader是項目開發(fā)中Java的關(guān)鍵但很少使用的組件之一。 就我個人而言,我從未在任何項目中擴展ClassLoader,但是擁有自己的可以自定義Java類加載的ClassLoader的想法讓我感到很興奮。

本文將概述Java類加載,然后繼續(xù)創(chuàng)建自定義ClassLoader并使用它。

什么是ClassLoader?

我們知道Java程序在Java虛擬機(JVM)上運行。 當(dāng)我們編譯Java類時,它會以字節(jié)碼的形式將其轉(zhuǎn)換為平臺和機器無關(guān)的已編譯程序,并將其存儲為.class文件。 之后,當(dāng)我們嘗試使用類時,Java ClassLoader將該類加載到內(nèi)存中。

Java中內(nèi)置了三種類型的內(nèi)置類加載器:

  • Bootstrap類加載器 –它加載JDK內(nèi)部類,通常加載rt.jar和其他核心類,例如java.lang。*包類
  • 擴展類加載器 –它從JDK擴展目錄(通常為$ JAVA_HOME / lib / ext目錄)中加載類。
  • 系統(tǒng)類加載器 –它從當(dāng)前類路徑加載類,這些類可以在使用-cp或-classpath命令行選項調(diào)用程序時進行設(shè)置。
  • 讓我們通過執(zhí)行以下java程序更好地理解這一點:

    ClassLoaderTest.java

    package com.journaldev.classloader;public class ClassLoaderTest {public static void main(String[] args) {System.out.println("class loader for HashMap: "+ java.util.HashMap.class.getClassLoader());System.out.println("class loader for DNSNameService: "+ sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());System.out.println("class loader for this class: "+ ClassLoaderTest.class.getClassLoader());System.out.println(com.mysql.jdbc.Blob.class.getClassLoader());}}

    上面程序的輸出:

    HashMap的類加載器:null
    DNSNameService的類加載器:sun.misc.Launcher$ExtClassLoader@51f12c4e
    此類的類加載器:sun.misc.Launcher$AppClassLoader@799134f4
    sun.misc.Launcher$AppClassLoader@799134f4

    如您所見,java.util.HashMap ClassLoader作為null來反映Bootstrap ClassLoader,而DNSNameService ClassLoader是ExtClassLoader。 由于類本身位于CLASSPATH中,因此System ClassLoader會加載它。

    當(dāng)我們嘗試加載HashMap時,我們的System ClassLoader將其委托給Extension ClassLoader,后者再將其委托給找到該類的Bootstrap ClassLoader并將其加載到JVM中。 DNSNameService類遵循相同的過程,但是Bootstrap ClassLoader無法定位它,因為它位于$ JAVA_HOME / lib / ext / dnsns.jar中,因此由擴展類加載器加載。

    還有一點要注意的是,子類加載器加載的類可以查看其父類加載器加載的類。 因此,由System ClassLoader加載的類具有對Extensions和Bootstrap ClassLoader加載的類的可見性。

    如果有同級類加載器,則它們將無法訪問彼此加載的類。

    為什么要編寫ClassLoader?

    Java默認(rèn)的ClassLoader可以從本地文件系統(tǒng)中加載文件,這在大多數(shù)情況下已經(jīng)足夠了。 但是,如果在加載類時希望在運行時或從FTP服務(wù)器或通過第三方Web服務(wù)獲取類,則必須擴展現(xiàn)有的類加載器。 例如,AppletViewers從遠(yuǎn)程Web服務(wù)器加載類。

    ClassLoader如何工作?

    當(dāng)JVM請求一個類時,它通過傳遞類的完全分類名稱來調(diào)用ClassLoader的loadClass函數(shù)。

    loadClass函數(shù)調(diào)用findLoadedClass()方法來檢查該類是否已加載。 需要避免多次加載該類。

    如果尚未加載該類,則它將把請求委派給父ClassLoader以加載該類。

    如果父ClassLoader找不到該Class,則它將調(diào)用findClass()方法在文件系統(tǒng)中查找這些類。

    創(chuàng)建我們自己的ClassLoader

    我們將通過擴展ClassLoader類并覆蓋loadClass(String name)函數(shù)來創(chuàng)建自己的ClassLoader。 如果名稱以com.journaldev(即我們的示例類包)開頭,則將使用我們自己的類加載器加載它,否則我們將調(diào)用父ClassLoader loadClass()方法加載該類。

    項目結(jié)構(gòu)如下圖所示:

    CCLoader.java:這是我們的自定義類加載器,具有以下方法。

    1. 專用字節(jié)[] loadClassFileData(字符串名稱)

    此方法將從文件系統(tǒng)讀取類文件到字節(jié)數(shù)組。

    2. 私有類getClass(String name)

    此方法將調(diào)用loadClassFileData()函數(shù),并通過調(diào)用父defineClass()方法,它將生成Class并返回它。

    3. public Class loadClass(字符串名稱)

    此方法負(fù)責(zé)加載Class。 如果類名以com.journaldev(我們的示例類)開頭,則它將使用getClass()方法加載它,否則它將調(diào)用父loadClass函數(shù)來加載它。

    4. public CCLoader(ClassLoader的父對象)

    這是負(fù)責(zé)設(shè)置父ClassLoader的構(gòu)造函數(shù)。

    CCLoader源代碼:

    import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream;/*** Our Custom Class Loader to load the classes. Any class in the com.journaldev* package will be loaded using this ClassLoader. For other classes, it will* delegate the request to its Parent ClassLoader.**/ public class CCLoader extends ClassLoader {/*** This constructor is used to set the parent ClassLoader*/public CCLoader(ClassLoader parent) {super(parent);}/*** Loads the class from the file system. The class file should be located in* the file system. The name should be relative to get the file location** @param name* Fully Classified name of class, for example com.journaldev.Foo*/private Class getClass(String name) throws ClassNotFoundException {String file = name.replace('.', File.separatorChar) + ".class";byte[] b = null;try {// This loads the byte code data from the fileb = loadClassFileData(file);// defineClass is inherited from the ClassLoader class// that converts byte array into a Class. defineClass is Final// so we cannot override itClass c = defineClass(name, b, 0, b.length);resolveClass(c);return c;} catch (IOException e) {e.printStackTrace();return null;}}/*** Every request for a class passes through this method. If the class is in* com.journaldev package, we will use this classloader or else delegate the* request to parent classloader.*** @param name* Full class name*/@Overridepublic Class loadClass(String name) throws ClassNotFoundException {System.out.println("Loading Class '" + name + "'");if (name.startsWith("com.journaldev")) {System.out.println("Loading Class using CCLoader");return getClass(name);}return super.loadClass(name);}/*** Reads the file (.class) into a byte array. The file should be* accessible as a resource and make sure that its not in Classpath to avoid* any confusion.** @param name* File name* @return Byte array read from the file* @throws IOException* if any exception comes in reading the file*/private byte[] loadClassFileData(String name) throws IOException {InputStream stream = getClass().getClassLoader().getResourceAsStream(name);int size = stream.available();byte buff[] = new byte[size];DataInputStream in = new DataInputStream(stream);in.readFully(buff);in.close();return buff;} }

    CCRun.java:

    這是帶有主要功能的測試類,我們在其中創(chuàng)建ClassLoader的對象并使用其loadClass方法加載示例類。 加載該類后,我們將使用Java Reflection API來調(diào)用其方法。

    import java.lang.reflect.Method;public class CCRun {public static void main(String args[]) throws Exception {String progClass = args[0];String progArgs[] = new String[args.length - 1];System.arraycopy(args, 1, progArgs, 0, progArgs.length);CCLoader ccl = new CCLoader(CCRun.class.getClassLoader());Class clas = ccl.loadClass(progClass);Class mainArgType[] = { (new String[0]).getClass() };Method main = clas.getMethod("main", mainArgType);Object argsArray[] = { progArgs };main.invoke(null, argsArray);// Below method is used to check that the Foo is getting loaded// by our custom class loader i.e CCLoaderMethod printCL = clas.getMethod("printCL", null);printCL.invoke(null, new Object[0]);}}

    Foo.java和Bar.java:

    這些是由我們的自定義類加載器加載的測試類。 它們還具有一個printCL()方法,該方法將被調(diào)用以打印已加載該類的ClassLoader。 Foo類將由我們的自定義類加載器加載,后者又使用Bar類,因此Bar類也將由我們的自定義類加載器加載。

    Foo.java源代碼:

    package com.journaldev.cl;public class Foo {static public void main(String args[]) throws Exception {System.out.println("Foo Constructor " + args[0] + " " + args[1]);Bar bar = new Bar(args[0], args[1]);bar.printCL();}public static void printCL() {System.out.println("Foo ClassLoader: "+Foo.class.getClassLoader());} }

    Bar.java源代碼:

    package com.journaldev.cl;public class Bar {public Bar(String a, String b) {System.out.println("Bar Constructor " + a + " " + b);}public void printCL() {System.out.println("Bar ClassLoader: "+Bar.class.getClassLoader());} }

    執(zhí)行步驟:

    首先,我們將通過命令行編譯所有類。 之后,我們將通過傳遞三個參數(shù)來運行CCRun類。 第一個參數(shù)是Foo類的完全分類名稱,它將由我們的類加載器加載。 其他兩個參數(shù)將傳遞給Foo類的主函數(shù)和Bar構(gòu)造函數(shù)。 輸出的執(zhí)行步驟如下所示。

    Pankaj $ javac -cp。 com / journaldev / cl / Foo.java
    Pankaj $ javac -cp。 com / journaldev / cl / Bar.java
    Pankaj $ javac CCLoader.java
    Pankaj $ javac CCRun.java
    CCRun.java:18:警告:對最后一個參數(shù)使用不精確參數(shù)類型的varargs方法的非varargs調(diào)用;
    轉(zhuǎn)換為java.lang.Class以進行varargs調(diào)用
    強制轉(zhuǎn)換為java.lang.Class []進行非可變參數(shù)調(diào)用并禁止顯示此警告
    方法printCL = clas.getMethod(“ printCL”,null);
    ^
    1警告
    Pankaj $ java CCRun com.journaldev.cl.Foo 1212 1313
    正在加載類“ com.journaldev.cl.Foo”
    使用CCLoader加載類
    加載類“ java.lang.Object”
    加載類“ java.lang.String”
    加載類“ java.lang.Exception”
    加載類“ java.lang.System”
    加載類“ java.lang.StringBuilder”
    加載類'java.io.PrintStream'
    Foo構(gòu)造函數(shù)1212 1313
    加載類“ com.journaldev.cl.Bar”
    使用CCLoader加載類
    酒吧建設(shè)者1212 1313
    加載類“ java.lang.Class”
    欄類加載器:CCLoader @ 71f6f0bf
    Foo ClassLoader:CCLoader @ 71f6f0bf
    ctk-pcs1313512-2:src pk93229 $

    如果仔細(xì)查看輸出,則首先嘗試加載com.journaldev.cl.Foo類,但是由于擴展了java.lang.Object類,因此嘗試首先加載它,并請求將其委托給CCLoader loadClass方法它到父類。 因此,父類加載器正在加載Object,String和其他Java類。 我們的ClassLoader僅從文件系統(tǒng)加載Foo和Bar類,而當(dāng)我們調(diào)用它們的printCL()函數(shù)時,該文件系統(tǒng)將變得很清晰。

    請注意,我們可以更改loadClassFileData()功能以從FTP服務(wù)器讀取字節(jié)數(shù)組,或者通過調(diào)用任何第三方服務(wù)來即時獲取類字節(jié)數(shù)組。

    參考: Java面試問題:從我們的JCG合作伙伴Pankaj 了解和擴展Java ClassLoader 。

    相關(guān)文章:

    • JDK中的設(shè)計模式
    • Java內(nèi)存模型–快速概述和注意事項
    • Java Fork / Join進行并行編程
    • 依賴注入–手動方式

    翻譯自: https://www.javacodegeeks.com/2011/03/understanding-extending-java.html

    總結(jié)

    以上是生活随笔為你收集整理的了解和扩展Java ClassLoader的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。