<dfn id="w48us"></dfn><ul id="w48us"></ul>
  • <ul id="w48us"></ul>
  • <del id="w48us"></del>
    <ul id="w48us"></ul>
  • Java ClassLoader原理深入講解

    時(shí)間:2024-10-19 00:14:48 JAVA認(rèn)證 我要投稿
    • 相關(guān)推薦

    Java ClassLoader原理深入講解

      JAVA啟動(dòng)后,是經(jīng)過(guò)JVM各級(jí)ClassLoader來(lái)加載各個(gè)類(lèi)到內(nèi)存。為了更加了解加載過(guò)程,下面為大家深入講解Java ClassLoader原理,僅供參考!

      當(dāng)JVM(Java虛擬機(jī))啟動(dòng)時(shí),會(huì)形成由三個(gè)類(lèi)加載器組成的初始類(lèi)加載器層次結(jié)構(gòu):

      bootstrap classloader

      |

      extension classloader

      |

      system classloader

      bootstrap classloader -引導(dǎo)(也稱(chēng)為原始)類(lèi)加載器,它負(fù)責(zé)加載Java的核心類(lèi)。在Sun的JVM中,在執(zhí)行java的命令中使用-Xbootclasspath選項(xiàng)或使用 - D選項(xiàng)指定sun.boot.class.path系統(tǒng)屬性值可以指定附加的類(lèi)。這個(gè)加載器的是非常特殊的,它實(shí)際上不是 java.lang.ClassLoader的子類(lèi),而是由JVM自身實(shí)現(xiàn)的。大家可以通過(guò)執(zhí)行以下代碼來(lái)獲得bootstrap classloader加載了那些核心類(lèi)庫(kù):

      URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

      for (int i = 0; i < urls.length; i++) {

      System.out.println(urls.toExternalform());

      }

      在我的計(jì)算機(jī)上的結(jié)果為:

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/jce.jar

      文件:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar

      文件:/C:/j2sdk1.4.1_01/jre/classes

      這時(shí)大家知道了為什么我們不需要在系統(tǒng)屬性CLASSPATH中指定這些類(lèi)庫(kù)了吧,因?yàn)镴VM在啟動(dòng)的時(shí)候就自動(dòng)加載它們了。

      extension classloader -擴(kuò)展類(lèi)加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統(tǒng)屬性指定的)中JAR的 類(lèi)包。這為引入除Java核心類(lèi)以外的新功能提供了一個(gè)標(biāo)準(zhǔn)機(jī)制。因?yàn)槟J(rèn)的擴(kuò)展目錄對(duì)所有從同一個(gè)JRE中啟動(dòng)的JVM都是通用的,所以放入這個(gè)目錄的 JAR類(lèi)包對(duì)所有的JVM和system classloader都是可見(jiàn)的。在這個(gè)實(shí)例上調(diào)用方法getParent()總是返回空值null,因?yàn)橐龑?dǎo)加載器bootstrap classloader不是一個(gè)真正的ClassLoader實(shí)例。所以當(dāng)大家執(zhí)行以下代碼時(shí):

      System.out.println(System.getProperty("java.ext.dirs"));

      ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();

      System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());

      結(jié)果為:

      C:/j2sdk1.4.1_01/jre/lib/ext

      the parent of extension classloader : null

      extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一個(gè)實(shí)際的classloader,所以為null。

      system classloader -系統(tǒng)(也稱(chēng)為應(yīng)用)類(lèi)加載器,它負(fù)責(zé)在JVM被啟動(dòng)時(shí),加載來(lái)自在命令java中的-classpath或者java.class.path系統(tǒng)屬性或 者 CLASSPATH操作系統(tǒng)屬性所指定的JAR類(lèi)包和類(lèi)路徑?偰芡ㄟ^(guò)靜態(tài)方法ClassLoader.getSystemClassLoader()找 到該類(lèi)加載器。如果沒(méi)有特別指定,則用戶自定義的任何類(lèi)加載器都將該類(lèi)加載器作為它的父加載器。執(zhí)行以下代碼即可獲得:

      System.out.println(System.getProperty("java.class.path"));

      輸出結(jié)果則為用戶在系統(tǒng)屬性里面設(shè)置的CLASSPATH。

      classloader 加載類(lèi)用的是全盤(pán)負(fù)責(zé)委托機(jī)制。所謂全盤(pán)負(fù)責(zé),即是當(dāng)一個(gè)classloader加載一個(gè)Class的時(shí)候,這個(gè)Class所依賴的和引用的所有 Class也由這個(gè)classloader負(fù)責(zé)載入,除非是顯式的使用另外一個(gè)classloader載入;委托機(jī)制則是先讓parent(父)類(lèi)加載器 (而不是super,它與parent classloader類(lèi)不是繼承關(guān)系)尋找,只有在parent找不到的時(shí)候才從自己的類(lèi)路徑中去尋找。此外類(lèi)加載還采用了cache機(jī)制,也就是如果 cache中保存了這個(gè)Class就直接返回它,如果沒(méi)有才從文件中讀取和轉(zhuǎn)換成Class,并存入cache,這就是為什么我們修改了Class但是必 須重新啟動(dòng)JVM才能生效的原因。

      每個(gè)ClassLoader加載Class的過(guò)程是:

      1.檢測(cè)此Class是否載入過(guò)(即在cache中是否有此Class),如果有到8,如果沒(méi)有到2

      2.如果parent classloader不存在(沒(méi)有parent,那parent一定是bootstrap classloader了),到4

      3.請(qǐng)求parent classloader載入,如果成功到8,不成功到5

      4.請(qǐng)求jvm從bootstrap classloader中載入,如果成功到8

      5.尋找Class文件(從與此classloader相關(guān)的類(lèi)路徑中尋找)。如果找不到則到7.

      6.從文件中載入Class,到8.

      7.拋出ClassNotFoundException.

      8.返回Class.

      其中5.6步我們可以通過(guò)覆蓋ClassLoader的findClass方法來(lái)實(shí)現(xiàn)自己的載入策略。甚至覆蓋loadClass方法來(lái)實(shí)現(xiàn)自己的載入過(guò)程。

      類(lèi)加載器的順序是:

      先 是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家會(huì)發(fā)現(xiàn)加載的Class越是重要的越在靠前面。這樣做的原因是出于安全性的考慮,試想如果system classloader“親自”加載了一個(gè)具有破壞性的“java.lang.System”類(lèi)的后果吧。這種委托機(jī)制保證了用戶即使具有一個(gè)這樣的類(lèi), 也把它加入到了類(lèi)路徑中,但是它永遠(yuǎn)不會(huì)被載入,因?yàn)檫@個(gè)類(lèi)總是由bootstrap classloader來(lái)加載的。大家可以執(zhí)行一下以下的代碼:

      System.out.println(System.class.getClassLoader());

      將會(huì)看到結(jié)果是null,這就表明java.lang.System是由bootstrap classloader加載的,因?yàn)閎ootstrap classloader不是一個(gè)真正的ClassLoader實(shí)例,而是由JVM實(shí)現(xiàn)的,正如前面已經(jīng)說(shuō)過(guò)的。

      下面就讓我們來(lái)看看JVM是如何來(lái)為我們來(lái)建立類(lèi)加載器的結(jié)構(gòu)的:

      sun.misc.Launcher,顧名思義,當(dāng)你執(zhí)行java命令的時(shí)候,JVM會(huì)先使用bootstrap classloader載入并初始化一個(gè)Launcher,執(zhí)行下來(lái)代碼:

      System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());

      結(jié)果為:

      the Launcher's classloader is null (因?yàn)槭怯胋ootstrap classloader加載,所以class loader為null)

      Launcher 會(huì)根據(jù)系統(tǒng)和命令設(shè)定初始化好class loader結(jié)構(gòu),JVM就用它來(lái)獲得extension classloader和system classloader,并載入所有的需要載入的Class,最后執(zhí)行java命令指定的帶有靜態(tài)的main方法的Class。extension classloader實(shí)際上是sun.misc.Launcher$ExtClassLoader類(lèi)的一個(gè)實(shí)例,system classloader實(shí)際上是sun.misc.Launcher$AppClassLoader類(lèi)的一個(gè)實(shí)例。并且都是 java.net.URLClassLoader的子類(lèi)。

      讓我們來(lái)看看Launcher初試化的過(guò)程的部分代碼。

      Launcher的部分代碼:

      public class Launcher {

      public Launcher() {

      ExtClassLoader extclassloader;

      try {

      //初始化extension classloader

      extclassloader = ExtClassLoader.getExtClassLoader();

      } catch(IOException ioexception) {

      throw new InternalError("Could not create extension class loader");

      }

      try {

      //初始化system classloader,parent是extension classloader

      loader = AppClassLoader.getAppClassLoader(extclassloader);

      } catch(IOException ioexception1) {

      throw new InternalError("Could not create application class loader");

      }

      //將system classloader設(shè)置成當(dāng)前線程的context classloader(將在后面加以介紹)

      Thread.currentThread().setContextClassLoader(loader);

      ......

      }

      public ClassLoader getClassLoader() {

      //返回system classloader

      return loader;

      }

      }

      extension classloader的部分代碼:

      static class Launcher$ExtClassLoader extends URLClassLoader {

      public static Launcher$ExtClassLoader getExtClassLoader()

      throws IOException

      {

      File afile[] = getExtDirs();

      return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));

      }

      private static File[] getExtDirs() {

      //獲得系統(tǒng)屬性“java.ext.dirs”

      String s = System.getProperty("java.ext.dirs");

      File afile[];

      if(s != null) {

      StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);

      int i = stringtokenizer.countTokens();

      afile = new File;

      for(int j = 0; j < i; j++)

      afile[j] = new File(stringtokenizer.nextToken());

      } else {

      afile = new File[0];

      }

      return afile;

      }

      }

      system classloader的部分代碼:

      static class Launcher$AppClassLoader extends URLClassLoader

      {

      public static ClassLoader getAppClassLoader(ClassLoader classloader)

      throws IOException

      {

      //獲得系統(tǒng)屬性“java.class.path”

      String s = System.getProperty("java.class.path");

      File afile[] = s != null ? Launcher.access$200(s) : new File[0];

      return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));

      }

      }

      看 了源代碼大家就清楚了吧,extension classloader是使用系統(tǒng)屬性“java.ext.dirs”設(shè)置類(lèi)搜索路徑的,并且沒(méi)有parent。system classloader是使用系統(tǒng)屬性“java.class.path”設(shè)置類(lèi)搜索路徑的,并且有一個(gè)parent classloader。Launcher初始化extension classloader,system classloader,并將system classloader設(shè)置成為context classloader,但是僅僅返回system classloader給JVM。

      這里怎么又出來(lái)一個(gè)context classloader呢?它有什么用呢?我們?cè)诮⒁粋(gè)線程Thread的時(shí)候,可以為這個(gè)線程通過(guò)setContextClassLoader方法來(lái) 指定一個(gè)合適的classloader作為這個(gè)線程的context classloader,當(dāng)此線程運(yùn)行的時(shí)候,我們可以通過(guò)getContextClassLoader方法來(lái)獲得此context classloader,就可以用它來(lái)載入我們所需要的Class。默認(rèn)的是system classloader。利用這個(gè)特性,我們可以“打破”classloader委托機(jī)制了,父classloader可以獲得當(dāng)前線程的context classloader,而這個(gè)context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以從其獲得所需的 Class,這就打破了只能向父classloader請(qǐng)求的限制了。這個(gè)機(jī)制可以滿足當(dāng)我們的classpath是在運(yùn)行時(shí)才確定,并由定制的 classloader加載的時(shí)候,由system classloader(即在jvm classpath中)加載的class可以通過(guò)context classloader獲得定制的classloader并加載入特定的class(通常是抽象類(lèi)和接口,定制的classloader中是其實(shí)現(xiàn)),例 如web應(yīng)用中的servlet就是用這種機(jī)制加載的.

      好了,現(xiàn)在我們了解了classloader的結(jié)構(gòu)和工作原理,那么我們 如何實(shí)現(xiàn)在運(yùn)行時(shí)的動(dòng)態(tài)載入和更新呢?只要我們能夠動(dòng)態(tài)改變類(lèi)搜索路徑和清除classloader的cache中已經(jīng)載入的Class就行了,有兩個(gè)方 案,一是我們繼承一個(gè)classloader,覆蓋loadclass方法,動(dòng)態(tài)的尋找Class文件并使用defineClass方法來(lái);另一個(gè)則非常 簡(jiǎn)單實(shí)用,只要重新使用一個(gè)新的類(lèi)搜索路徑來(lái)new一個(gè)classloader就行了,這樣即更新了類(lèi)搜索路徑以便來(lái)載入新的Class,也重新生成了一 個(gè)空白的cache(當(dāng)然,類(lèi)搜索路徑不一定必須更改)。噢,太好了,我們幾乎不用做什么工作,java.netURLClassLoader正是一個(gè)符 合我們要求的classloader!我們可以直接使用或者繼承它就可以了!

      這是j2se1.4 API的doc中URLClassLoader的兩個(gè)構(gòu)造器的描述:

      URLClassLoader(URL[] urls)

      Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.

      URLClassLoader(URL[] urls, ClassLoader parent)

      Constructs a new URLClassLoader for the given URLs.

      其中URL[] urls就是我們要設(shè)置的類(lèi)搜索路徑,parent就是這個(gè)classloader的parent classloader,默認(rèn)的是system classloader。

      好,現(xiàn)在我們能夠動(dòng)態(tài)的載入Class了,這樣我們就可以利用newInstance方法來(lái)獲得一個(gè)Object。但我們?nèi)绾螌⒋薕bject造型呢?可以將此Object造型成它本身的Class嗎?

      首先讓我們來(lái)分析一下java源文件的編譯,運(yùn)行吧!javac命令是調(diào)用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來(lái)編譯:

      public static int compile(String as[]);

      public static int compile(String as[], PrintWriter printwriter);

      返回0表示編譯成功,字符串?dāng)?shù)組as則是我們用javac命令編譯時(shí)的參數(shù),以空格劃分。例如:

      javac -classpath c:/foo/bar.jar;. -d c:/ c:/Some.java

      則 字符串?dāng)?shù)組as為{"-classpath","c://foo//bar.jar;.","-d","c://","c://Some.java"}, 如果帶有PrintWriter參數(shù),則會(huì)把編譯信息出到這個(gè)指定的printWriter中。默認(rèn)的輸出是System.err。

      其中 Main是由JVM使用Launcher初始化的system classloader載入的,根據(jù)全盤(pán)負(fù)責(zé)原則,編譯器在解析這個(gè)java源文件時(shí)所發(fā)現(xiàn)的它所依賴和引用的所有Class也將由system classloader載入,如果system classloader不能載入某個(gè)Class時(shí),編譯器將拋出一個(gè)“cannot resolve symbol”錯(cuò)誤。

      所以首先編譯就通不過(guò),也就是編譯器無(wú)法編譯一個(gè)引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫(xiě)錯(cuò)誤或者沒(méi)有把所需類(lèi)庫(kù)放到CLASSPATH中,大家一定經(jīng)?吹竭@個(gè)“cannot resolve symbol”這個(gè)編譯錯(cuò)誤吧!

      其 次,就是我們把這個(gè)Class放到編譯路徑中,成功的進(jìn)行了編譯,然后在運(yùn)行的時(shí)候不把它放入到CLASSPATH中而利用我們自己的 classloader來(lái)動(dòng)態(tài)載入這個(gè)Class,這時(shí)候也會(huì)出現(xiàn)“java.lang.NoClassDefFoundError”的違例,為什么呢?

      我 們?cè)賮?lái)分析一下,首先調(diào)用這個(gè)造型語(yǔ)句的可執(zhí)行的Class一定是由JVM使用Launcher初始化的system classloader載入的,根據(jù)全盤(pán)負(fù)責(zé)原則,當(dāng)我們進(jìn)行造型的時(shí)候,JVM也會(huì)使用system classloader來(lái)嘗試載入這個(gè)Class來(lái)對(duì)實(shí)例進(jìn)行造型,自然在system classloader尋找不到這個(gè)Class時(shí)就會(huì)拋出“java.lang.NoClassDefFoundError”的違例。

      OK, 現(xiàn)在讓我們來(lái)總結(jié)一下,java文件的編譯和Class的載入執(zhí)行,都是使用Launcher初始化的system classloader作為類(lèi)載入器的,我們無(wú)法動(dòng)態(tài)的改變system classloader,更無(wú)法讓JVM使用我們自己的classloader來(lái)替換system classloader,根據(jù)全盤(pán)負(fù)責(zé)原則,就限制了編譯和運(yùn)行時(shí),我們無(wú)法直接顯式的使用一個(gè)system classloader尋找不到的Class,即我們只能使用Java核心類(lèi)庫(kù),擴(kuò)展類(lèi)庫(kù)和CLASSPATH中的類(lèi)庫(kù)中的Class。

      還 不死心!再嘗試一下這種情況,我們把這個(gè)Class也放入到CLASSPATH中,讓system classloader能夠識(shí)別和載入。然后我們通過(guò)自己的classloader來(lái)從指定的class文件中載入這個(gè)Class(不能夠委托 parent載入,因?yàn)檫@樣會(huì)被system classloader從CLASSPATH中將其載入),然后實(shí)例化一個(gè)Object,并造型成這個(gè)Class,這樣JVM也識(shí)別這個(gè)Class(因?yàn)?system classloader能夠定位和載入這個(gè)Class從CLASSPATH中),載入的也不是CLASSPATH中的這個(gè)Class,而是從 CLASSPATH外動(dòng)態(tài)載入的,這樣總行了吧!十分不幸的是,這時(shí)會(huì)出現(xiàn)“java.lang.ClassCastException”違例。

      為 什么呢?我們也來(lái)分析一下,不錯(cuò),我們雖然從CLASSPATH外使用我們自己的classloader動(dòng)態(tài)載入了這個(gè)Class,但將它的實(shí)例造型的時(shí) 候是JVM會(huì)使用system classloader來(lái)再次載入這個(gè)Class,并嘗試將使用我們的自己的classloader載入的Class的一個(gè)實(shí)例造型為system classloader載入的這個(gè)Class(另外的一個(gè))。大家發(fā)現(xiàn)什么問(wèn)題了嗎?也就是我們嘗試將從一個(gè)classloader載入的Class的一 個(gè)實(shí)例造型為另外一個(gè)classloader載入的Class,雖然這兩個(gè)Class的名字一樣,甚至是從同一個(gè)class文件中載入。但不幸的是JVM 卻認(rèn)為這個(gè)兩個(gè)Class是不同的,即JVM認(rèn)為不同的classloader載入的相同的名字的Class(即使是從同一個(gè)class文件中載入的)是 不同的!這樣做的原因我想大概也是主要出于安全性考慮,這樣就保證所有的核心Java類(lèi)都是system classloader載入的,我們無(wú)法用自己的classloader載入的相同名字的Class的實(shí)例來(lái)替換它們的實(shí)例。

      看到這里,聰明的讀者一定想到了該如何動(dòng)態(tài)載入我們的Class,實(shí)例化,造型并調(diào)用了吧!

      那 就是利用面向?qū)ο蟮幕咎匦灾坏亩嘈涡。我們把我們?dòng)態(tài)載入的Class的實(shí)例造型成它的一個(gè)system classloader所能識(shí)別的父類(lèi)就行了!這是為什么呢?我們還是要再來(lái)分析一次。當(dāng)我們用我們自己的classloader來(lái)動(dòng)態(tài)載入這我們只要把 這個(gè)Class的時(shí)候,發(fā)現(xiàn)它有一個(gè)父類(lèi)Class,在載入它之前JVM先會(huì)載入這個(gè)父類(lèi)Class,這個(gè)父類(lèi)Class是system classloader所能識(shí)別的,根據(jù)委托機(jī)制,它將由system classloader載入,然后我們的classloader再載入這個(gè)Class,創(chuàng)建一個(gè)實(shí)例,造型為這個(gè)父類(lèi)Class,注意了,造型成這個(gè)父類(lèi) Class的時(shí)候(也就是上溯)是面向?qū)ο蟮膉ava語(yǔ)言所允許的并且JVM也支持的,JVM就使用system classloader再次載入這個(gè)父類(lèi)Class,然后將此實(shí)例造型為這個(gè)父類(lèi)Class。大家可以從這個(gè)過(guò)程發(fā)現(xiàn)這個(gè)父類(lèi)Class都是由 system classloader載入的,也就是同一個(gè)class loader載入的同一個(gè)Class,所以造型的時(shí)候不會(huì)出現(xiàn)任何異常。而根據(jù)多形性,調(diào)用這個(gè)父類(lèi)的方法時(shí),真正執(zhí)行的是這個(gè)Class(非父類(lèi) Class)的覆蓋了父類(lèi)方法的方法。這些方法中也可以引用system classloader不能識(shí)別的Class,因?yàn)楦鶕?jù)全盤(pán)負(fù)責(zé)原則,只要載入這個(gè)Class的classloader即我們自己定義的 classloader能夠定位和載入這些Class就行了。

      這樣我們就可以事先定義好一組接口或者基類(lèi)并放入CLASSPATH中,然 后在執(zhí)行的時(shí)候動(dòng)態(tài)的載入實(shí)現(xiàn)或者繼承了這些接口或基類(lèi)的子類(lèi)。還不明白嗎?讓我們來(lái)想一想Servlet吧,web application server能夠載入任何繼承了Servlet的Class并正確的執(zhí)行它們,不管它實(shí)際的Class是什么,就是都把它們實(shí)例化成為一個(gè)Servlet Class,然后執(zhí)行Servlet的init,doPost,doGet和destroy等方法的,而不管這個(gè)Servlet是從web- inf/lib和web-inf/classes下由system classloader的子classloader(即定制的classloader)動(dòng)態(tài)載入。說(shuō)了這么多希望大家都明白了。在applet,ejb等 容器中,都是采用了這種機(jī)制.

      對(duì)于以上各種情況,希望大家實(shí)際編寫(xiě)一些example來(lái)實(shí)驗(yàn)一下。

      最后我再說(shuō)點(diǎn)別的, classloader雖然稱(chēng)為類(lèi)加載器,但并不意味著只能用來(lái)加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當(dāng)然,這些資源必 須在CLASSPATH中的jar類(lèi)庫(kù)中或目錄下。我們來(lái)看API的doc中關(guān)于ClassLoader的兩個(gè)尋找資源和Class的方法描述吧:

      public URL getResource(String name)

      用指定的名字來(lái)查找資源,一個(gè)資源是一些能夠被class代碼訪問(wèn)的在某種程度上依賴于代碼位置的數(shù)據(jù)(圖片,音頻,文本等等)。

      一個(gè)資源的名字是以'/'號(hào)分隔確定資源的路徑名的。

      這個(gè)方法將先請(qǐng)求parent classloader搜索資源,如果沒(méi)有parent,則會(huì)在內(nèi)置在虛擬機(jī)中的classloader(即bootstrap classloader)的路徑中搜索。如果失敗,這個(gè)方法將調(diào)用findResource(String)來(lái)尋找資源。

      public static URL getSystemResource(String name)

      從用來(lái)載入類(lèi)的搜索路徑中查找一個(gè)指定名字的資源。這個(gè)方法使用system class loader來(lái)定位資源。即相當(dāng)于ClassLoader.getSystemClassLoader().getResource(name)。

      例如:

      System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));

      的結(jié)果為:

      jar:文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class

      表明String.class文件在rt.jar的java/lang目錄中。

      因此我們可以將圖片等資源隨同Class一同打包到j(luò)ar類(lèi)庫(kù)中(當(dāng)然,也可單獨(dú)打包這些資源)并添加它們到class loader的搜索路徑中,我們就可以無(wú)需關(guān)心這些資源的具體位置,讓class loader來(lái)幫我們尋找了!

    【Java ClassLoader原理深入講解】相關(guān)文章:

    Java ClassLoader原理詳細(xì)分析201603-04

    2016最新java classloader詳解03-04

    小升初奧數(shù)知識(shí)講解之抽屜原理01-28

    揮桿原理03-23

    導(dǎo)游講解技巧04-08

    office知識(shí)講解03-18

    素描知識(shí)講解01-10

    JavaScript的課堂講解03-31

    學(xué)習(xí)Java的技巧03-05

    主站蜘蛛池模板: 欧美精品videosse精子| 国产精品55夜色66夜色| HEYZO无码综合国产精品227| 精品成人一区二区三区四区| 国产成人精品久久一区二区三区 | 91精品国产91久久| 国产精品区一区二区三在线播放 | 亚洲精品国产成人专区| 欧美精品久久久久久久自慰| 久久久久久久亚洲精品| 亚洲视频精品在线| 国产精品亚洲片夜色在线| 国产亚洲精品美女久久久| 亚洲精品国产精品乱码视色| 亚洲国产精品13p| 无翼乌无遮挡全彩老师挤奶爱爱帝国综合社区精品 | 久久久久人妻精品一区二区三区| 久久五月精品中文字幕| 国产精品久久久久久久久久免费| 亚洲国产精品婷婷久久| 青青青国产依人精品视频| 国产精品美女久久久| 999精品视频| 国产成人亚洲精品| 996久久国产精品线观看| 2021国产精品视频| 国产成人精品无码一区二区| 国产精品白丝AV网站| 国产麻豆一精品一AV一免费 | 亚洲无码精品浪潮| 日本五区在线不卡精品| 久久精品综合一区二区三区| 久久久久久无码国产精品中文字幕 | 亚洲国产精品人人做人人爽| 久久亚洲中文字幕精品一区四| 刺激无码在线观看精品视频| 国产精品 综合 第五页| 国产高清在线精品一本大道国产| segui久久国产精品| 国产欧美一区二区精品性色99| 国产精品女同一区二区久久|