欧美日韩不卡一区二区三区,www.蜜臀.com,高清国产一区二区三区四区五区,欧美日韩三级视频,欧美性综合,精品国产91久久久久久,99a精品视频在线观看

java語言

Java中的類加載器

時間:2025-03-09 07:44:10 java語言 我要投稿
  • 相關(guān)推薦

Java中的類加載器

  Java是一門面向?qū)ο缶幊陶Z言,那么大家知道 Java中的類加載器是什么呢?下面一起來看看!

  從java的動態(tài)性到類加載機(jī)制

  我們知道,Java是一種動態(tài)語言。那么怎樣理解這個“動態(tài)”呢?或者說一門語言具備了什么特性,才能稱之為動態(tài)語言呢?對于java,我是這樣理解的。

  我們都知道JVM(java虛擬機(jī))執(zhí)行的不是本地機(jī)器碼指令,而是執(zhí)行一種稱之為字節(jié)碼的指令(存在于class文件中)。這就要求虛擬機(jī)在真正執(zhí)行字節(jié)碼之前,先把相關(guān)的class文件加載到內(nèi)存中。虛擬機(jī)不是一次性加載所有需要的class文件,因為它在執(zhí)行的時候根本不會知道以后會用到哪些class文件。它是每用到一個類,就會在運(yùn)行時“動態(tài)地”加載和這個類相關(guān)的class文件。這就是java被稱之為動態(tài)性語言的根本原因。除了動態(tài)加載類之外,還會動態(tài)的初始化類,對類進(jìn)行動態(tài)鏈接。動態(tài)初始化和動態(tài)鏈接放在其他文章中進(jìn)行介紹。本文中只關(guān)心類的加載。

  在JVM中負(fù)責(zé)對類進(jìn)行加載的正是本文要介紹的類加載器(ClassLoader),所以,類加載器是JVM不可或缺的重要組件。

  java中的類加載器及類加載器工作原理

  java中(指的是javase)有三種類加載器。每個類加載器在創(chuàng)建的時候已經(jīng)指定他們對應(yīng)的目錄, 也就是說每個類加載器去哪里加載類是確定的,我認(rèn)為在ClassLoader類中應(yīng)該會有g(shù)etTargetPath()之類的方法, 得到他們對應(yīng)的路徑,找了找jdk的文檔,發(fā)現(xiàn)是沒有的。以下是這三種類加載器和他們對應(yīng)的路徑:

  * AppClassLoader -- 加載classpath指定的路徑中的類

  * ExtClassLoader -- 加載jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下的類

  * BootStrap -- 加載JRE/lib/rt.jar中的類

  那么類加載器是如何工作的呢?可以參看jdk中ClassLoader類的源碼。這個類的實現(xiàn)使用了模板方法模式,首先是loadClass方法來加載類,loadClass方法又調(diào)用了findClass方法,該方法讀取并返回類文件的數(shù)據(jù),findClass方法返回后,loadClass方法繼續(xù)調(diào)用defineClass方法,將返回的數(shù)據(jù)加工成虛擬機(jī)運(yùn)行時可識別的類型信息。所以,我們?nèi)绻_發(fā)自己的類加載器,只需要繼承jdk中的ClassLoader類,并覆蓋findClass方法就可以了,剩下的而工作,父類會完成。其他java平臺有的根據(jù)自己的需求,實現(xiàn)了自己特定的類加載器,例如javaee平臺中的tomcat服務(wù)器,Android平臺中的dalvik虛擬機(jī)也定義了自己的類加載器。

  虛擬機(jī)加載類有兩種方式,一種方式就是上面提到的ClassLoader.loadClass()方法,另一種是使用反射API,Class.forName()方法,其實Class.forName()方法內(nèi)部也是使用的ClassLoader。Class類中forName方法的實現(xiàn)如下:

  [java] view plain copypublic static Class forName(String name, boolean initialize,

  ClassLoader loader)

  throws ClassNotFoundException

  {

  if (loader == null) {

  SecurityManager sm = System.getSecurityManager();

  if (sm != null) {

  ClassLoader ccl = ClassLoader.getCallerClassLoader();

  if (ccl != null) {

  sm.checkPermission(

  SecurityConstants.GET_CLASSLOADER_PERMISSION);

  }

  }

  }

  return forName0(name, initialize, loader);

  }

  /** Called after security checks have been made. */

  private static native Class forName0(String name, boolean initialize,

  ClassLoader loader)

  throws ClassNotFoundException;

  類加載器的三個特性

  類加載器有三個特性,分別為委派,可見性和單一性,其他文章上對這三個特性的介紹如下:

  * 委托機(jī)制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類,那么再加載它。

  * 可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。

  * 單一性原理是指僅加載一個類一次,這是由委托機(jī)制確保子類加載器不會再次加載父類加載器加載過的類。

  其中,委派機(jī)制是基礎(chǔ),在其他資料中也把這種機(jī)制叫做類加載器的雙親委派模型,其實說的是同一個意思?杉有院蛦我恍允且蕾囉谖蓹C(jī)制的。

  以下代碼測試類加載器的委派機(jī)制:

  [java] view plain copyClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();

  System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f

  ClassLoader extClassLoader = appClassLoader.getParent();

  System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1

  //AppClassLoader的父加載器是ExtClassLoader

  System.out.println(extClassLoader.getParent()); //null

  //ExtClassLoader的父加載器是null, 也就是BootStrap,這是由c語言實現(xiàn)的

  由打印結(jié)果可知,加載我們自己編寫的類的加載器是AppClassLoader,AppClassLoader的父加載器是ExtClassLoader,在而ExtClassLoader的父加載器返回結(jié)果為null,這說明他的附加載器是BootStrap,這個加載器是和虛擬機(jī)緊密聯(lián)系在一起的,在虛擬機(jī)啟動時,就會加載jdk中的類。它是由C實現(xiàn)的,沒有對應(yīng)的java對象,所以返回null。但是在邏輯上,BootStrap仍是ExtClassLoader的父加載器。也就是說每當(dāng)ExtClassLoader加載一個類時,總會委托給BootStrap加載。

  系統(tǒng)類加載器和線程上下文類加載器

  在java中,還存在兩個概念,分別是系統(tǒng)類加載器和線程上下文類加載器。

  其實系統(tǒng)類加載器就是AppClassLoader應(yīng)用程序類加載器,它兩個值得是同一個加載器,以下代碼可以驗證:

  [java] view plain copyClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();

  System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f

  ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();

  System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f

  //由上面的驗證可知, 應(yīng)用程序類加載器和系統(tǒng)類加載器是相同的, 因為地址是一樣的

  這兩個類加載器對應(yīng)的輸出,不僅類名相同,連對象的哈希值都是一樣的,這充分說明系統(tǒng)類加載器和應(yīng)用程序類加載器不僅是同一個類,更是同一個類的同一個對象。

  每個線程都會有一個上下文類加載器,由于在線程執(zhí)行時加載用到的類,默認(rèn)情況下是父線程的上下文類加載器, 也就是AppClassLoader。

  [java] view plain copynew Thread(new Runnable() {

  @Override

  public void run() {

  ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();

  System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f

  }

  }).start();

  這個子線程在執(zhí)行時打印的信息為sun.misc.Launcher$AppClassLoader@19821f,可以看到和主線程中的AppClassLoader是同一個對象(哈希值相同)。

  也可以為線程設(shè)置特定的類加載器,這樣的話,線程在執(zhí)行時就會使用這個特定的類加載器來加載使用到的類。如下代碼:

  [java] view plain copyThread th = new Thread(new Runnable() {

  @Override

  public void run() {

  ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();

  System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74

  }

  });

  th.setContextClassLoader(new ClassLoader() {});

  th.start();

  在線程運(yùn)行之前,為它設(shè)置了一個匿名內(nèi)部類的類加載器對象,線程運(yùn)行時,輸出的信息為:jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74,也就是我們設(shè)置的那個類加載器對象。

  類加載器的可見性

  下面驗證類加載器的可見性,也就是 子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。

  以下代碼使用父加載器ExtClassLoader加載子加載器AppClassLoader路徑下的類,由輸出可知,是不可能實現(xiàn)的。

  [java] view plain copytry {

  Class.forName("jg.zhang.java.testConcurrent.Person", true,

  ClassLoaderTest.class.getClassLoader().getParent());

  System.out.println("1 -- 類被加載");

  } catch (ClassNotFoundException e) {

  //e.printStackTrace();

  System.out.println("1 -- 未找到類");

  }

  輸出為 :1 -- 未找到類 。說明拋出了ClassNotFoundException異常。原因是讓ExtClassLoader加載 jg.zhang.java.testConcurrent.Person這個類因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下,所以拋出ClassNotFoundException。所以父加載器不能加載應(yīng)該被子加載器加載的類。也就是說這個類在父加載器中不可見。這種機(jī)制依賴于委派機(jī)制。

  下面代碼使用子加載器AppClassLoader 加載父加載器BootStrap中的類,這是可以實現(xiàn)的。

  [java] view plain copytry {

  Class.forName("java.lang.String", true,

  ClassLoaderTest.class.getClassLoader());

  System.out.println("2 -- 類被加載");

  } catch (ClassNotFoundException e) {

  //e.printStackTrace();

  System.out.println("2 -- 未找到類");

  }

  輸出為:2 -- 類被加載。說明成功加載了String類。是因為在指定由AppClassLoader加載String類時,由AppClassLoader一直委派到BootStrap加載。雖然是由子加載器的父加載器加載的,但是也可以說,父加載器加載的類對于子加載器來說是可見的。這同樣依賴于委派機(jī)制。其實在虛擬機(jī)啟動初期,java.lang.String已經(jīng)被BootStrap預(yù)加載了,這時再次加載,虛擬機(jī)發(fā)現(xiàn)已經(jīng)加載,不會再重復(fù)加載。這同時也證明了類加載器的單一性。

  測試代碼

  到此為止,類加載器的知識就全部講完了。以下是整個測試代碼:

  [java] view plain copypackage jg.zhang.java.testclassloader;

  * Java類加載器基于三個機(jī)制:委托、可見性和單一性。

  * 委托機(jī)制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類,那么再加載它。

  * 可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。

  * 單一性原理是指僅加載一個類一次,這是由委托機(jī)制確保子類加載器不會再次加載父類加載器加載過的類。

  *

  * 三種類加載器: 每個類加載器在創(chuàng)建的時候已經(jīng)指定他們對應(yīng)的目錄, 也就是說每個類加載器去哪里加載類是確定的

  * 我認(rèn)為在ClassLoader類中應(yīng)該會有g(shù)etTargetPath()之類的方法, 得到他們對應(yīng)的路徑,找了找jdk的文檔,發(fā)現(xiàn)是沒有的.

  * AppClassLoader -- 加載classpath指定的路徑中的類

  * ExtClassLoader -- 加載jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下的類

  * BootStrap -- 加載JRE/lib/rt.jar中的類

  *

  *

  *

  * @author zhangjg

  *

  */

  public class ClassLoaderTest {

  public static void main(String[] args) {

  test1();

  test2();

  test3();

  }

  /**

  * 驗證線程上下文類加載器

  */

  private static void test3() {

  /**

  * 1 每個線程都會有一個上下文類加載器,由于在線程執(zhí)行時加載用到的類,默認(rèn)情況下是父線程

  * 的上下文類加載器, 也就是AppClassLoader

  */

  new Thread(new Runnable() {

  @Override

  public void run() {

  ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();

  System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f

  }

  }).start();

  /**

  * 2 也可以給創(chuàng)建的線程設(shè)定特定的上下文類加載器

  */

  Thread th = new Thread(new Runnable() {

  @Override

  public void run() {

  ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();

  System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74

  }

  });

  th.setContextClassLoader(new ClassLoader() {});

  th.start();

  }

  /**

  * 測試可見性,可見性依賴于委托機(jī)制

  */

  private static void test2() {

  /**

  * 1 讓ExtClassLoader加載 jg.zhang.java.testConcurrent.Person這個類

  * 因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統(tǒng)屬性定義的目錄下

  * 所以拋出ClassNotFoundException

  *

  * 所以父加載器不能加載應(yīng)該被子加載器加載的類,這個類在父加載器中不可見

  * 這種機(jī)制依賴于委派機(jī)制

  */

  try {

  Class.forName("jg.zhang.java.testConcurrent.Person", true,

  ClassLoaderTest.class.getClassLoader().getParent());

  System.out.println("1 -- 類被加載");

  } catch (ClassNotFoundException e) {

  //e.printStackTrace();

  System.out.println("1 -- 未找到類");

  }

  /**

  * 2 讓AppClassLoader加載java.lang.String類

  * 沒有拋出異常,說明類被正常加載了

  * 雖然是由AppClassLoader一直委派到BootStrap而加載的

  * 所以可以說,父加載器加載的類對于子加載器來說是可見的,這同樣依賴于委派機(jī)制

  *

  * 其實在虛擬機(jī)啟動初期,java.lang.String已經(jīng)被BootStrap預(yù)加載了

  * 這時再次加載,虛擬機(jī)發(fā)現(xiàn)已經(jīng)加載,不會再重復(fù)加載

  */

  try {

  Class.forName("java.lang.String", true,

  ClassLoaderTest.class.getClassLoader());

  System.out.println("2 -- 類被加載");

  } catch (ClassNotFoundException e) {

  //e.printStackTrace();

  System.out.println("2 -- 未找到類");

  }

  }

  /**

  * 驗證三種類加載器的父子關(guān)系

  */

  private static void test1() {

  ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();

  System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f

  ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();

  System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f

  //由上面的驗證可知, 應(yīng)用程序類加載器和系統(tǒng)類加載器是相同的, 因為地址是一樣的

  ClassLoader extClassLoader = appClassLoader.getParent();

  System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1

  //AppClassLoader的父加載器是ExtClassLoader

  System.out.println(extClassLoader.getParent()); //null

  //ExtClassLoader的父加載器是null, 也就是BootStrap,這是由c語言實現(xiàn)的

  }

  }

【 Java中的類加載器】相關(guān)文章:

java類加載器05-23

Java類加載器類介紹06-13

Java的內(nèi)部類與類的加載器06-24

Java中Number 與 Math 類05-20

Java中的Collection類概述06-22

Java中的抽象類07-15

Java中String類的方法07-24

java中File類的使用方法07-21

Java中ArrayList類的使用用法07-12