欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 類加載機制詳細介紹

 更新時間:2017年05月10日 11:12:57   投稿:lqh  
這篇文章主要介紹了Java 類加載機制詳細介紹的相關資料,需要的朋友可以參考下

一、類加載器

  類加載器(ClassLoader),顧名思義,即加載類的東西。在我們使用一個類之前,JVM需要先將該類的字節(jié)碼文件(.class文件)從磁盤、網(wǎng)絡或其他來源加載到內(nèi)存中,并對字節(jié)碼進行解析生成對應的Class對象,這就是類加載器的功能。我們可以利用類加載器,實現(xiàn)類的動態(tài)加載。

二、類的加載機制

  在Java中,采用雙親委派機制來實現(xiàn)類的加載。那什么是雙親委派機制?在Java Doc中有這樣一段描述:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance
 of ClassLoader has an associated parent class loader. When requested to find a class or resource,
 a ClassLoader instance will delegate the search for the class or resource to its parent class loader
 before attempting to find the class or resource itself. The virtual machine's built-in class loader,
 called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a
 ClassLoader instance.

 從以上描述中,我們可以總結(jié)出如下四點:

1、類的加載過程采用委托模式實現(xiàn)

2、每個 ClassLoader 都有一個父加載器。

3、類加載器在加載類之前會先遞歸的去嘗試使用父加載器加載。

4、虛擬機有一個內(nèi)建的啟動類加載器(bootstrap ClassLoader),該加載器沒有父加載器,但是可以作為其他加載器的父加載器。

   Java 提供三種類型的系統(tǒng)類加載器。第一種是啟動類加載器,由C++語言實現(xiàn),屬于JVM的一部分,其作用是加載 <Java_Runtime_Home>/lib 目錄中的文件,并且該類加載器只加載特定名稱的文件(如 rt.jar),而不是該目錄下所有的文件。另外兩種是 Java 語言自身實現(xiàn)的類加載器,包括擴展類加載器(ExtClassLoader)和應用類加載器(AppClassLoader),擴展類加載器負責加載<Java_Runtime_Home>\lib\ext目錄中或系統(tǒng)變量 java.ext.dirs 所指定的目錄中的文件。應用程序類加載器負責加載用戶類路徑中的文件。用戶可以直接使用擴展類加載器或系統(tǒng)類加載器來加載自己的類,但是用戶無法直接使用啟動類加載器,除了這兩種類加載器以外,用戶也可以自定義類加載器,加載流程如下圖所示:

  注意:這里父類加載器并不是通過繼承關系來實現(xiàn)的,而是采用組合實現(xiàn)的。

  我們可以通過一段程序來驗證這個過程:

public class Test {
}
 
public class TestMain {
  public static void main(String[] args) {
 
    ClassLoader loader = Test.class.getClassLoader();
    while (loader!=null){
      System.out.println(loader);
      loader = loader.getParent();
    }
  }
}

  上面程序的運行結(jié)果如下所示:  

  從結(jié)果我們可以看出,默認情況下,用戶自定義的類使用 AppClassLoader 加載,AppClassLoader 的父加載器為 ExtClassLoader,但是 ExtClassLoader 的父加載器卻顯示為空,這是什么原因呢?究其緣由,啟動類加載器屬于 JVM 的一部分,它不是由 Java 語言實現(xiàn)的,在 Java 中無法直接引用,所以才返回空。但如果是這樣,該怎么實現(xiàn) ExtClassLoader 與 啟動類加載器之間雙親委派機制?我們可以參考一下源碼:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
  {
    synchronized (getClassLoadingLock(name)) {
      // First, check if the class has already been loaded
      Class<?> c = findLoadedClass(name);
      if (c == null) {
        long t0 = System.nanoTime();
        try {
          if (parent != null) {
            c = parent.loadClass(name, false);
          } else {
            c = findBootstrapClassOrNull(name);
          }
        } catch (ClassNotFoundException e) {
          // ClassNotFoundException thrown if class not found
          // from the non-null parent class loader
        }
 
        if (c == null) {
          // If still not found, then invoke findClass in order
          // to find the class.
          long t1 = System.nanoTime();
          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;
    }
  }

  從源碼可以看出,ExtClassLoader 和 AppClassLoader都繼承自 ClassLoader 類,ClassLoader 類中通過 loadClass 方法來實現(xiàn)雙親委派機制。整個類的加載過程可分為如下三步:

  1、查找對應的類是否已經(jīng)加載。

  2、若未加載,則判斷當前類加載器的父加載器是否為空,不為空則委托給父類去加載,否則調(diào)用啟動類加載器加載(findBootstrapClassOrNull 再往下會調(diào)用一個 native 方法)。

  3、若第二步加載失敗,則調(diào)用當前類加載器加載。

  通過上面這段程序,可以很清楚的看出擴展類加載器與啟動類加載器之間是如何實現(xiàn)委托模式的。

      現(xiàn)在,我們再驗證另一個問題。我們將剛才的Test類打成jar包,將其放置在 <Java_Runtime_Home>\lib\ext 目錄下,然后再次運行上面的代碼,結(jié)果如下:

     現(xiàn)在,該類就不再通過 AppClassLoader 來加載,而是通過 ExtClassLoader 來加載了。如果我們試圖把jar包拷貝到<Java_Runtime_Home>\lib,嘗試通過啟動類加載器加載該類時,我們會發(fā)現(xiàn)編譯器無法識別該類,因為啟動類加載器除了指定目錄外,還必須是特定名稱的文件才能加載。

三、自定義類加載器

  通常情況下,我們都是直接使用系統(tǒng)類加載器。但是,有的時候,我們也需要自定義類加載器。比如應用是通過網(wǎng)絡來傳輸 Java 類的字節(jié)碼,為保證安全性,這些字節(jié)碼經(jīng)過了加密處理,這時系統(tǒng)類加載器就無法對其進行加載,這樣則需要自定義類加載器來實現(xiàn)。自定義類加載器一般都是繼承自 ClassLoader 類,從上面對 loadClass 方法來分析來看,我們只需要重寫 findClass 方法即可。下面我們通過一個示例來演示自定義類加載器的流程:

package com.paddx.test.classloading;
 
import java.io.*;
 
/**
 * Created by liuxp on 16/3/12.
 */
public class MyClassLoader extends ClassLoader {
 
  private String root;
 
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    byte[] classData = loadClassData(name);
    if (classData == null) {
      throw new ClassNotFoundException();
    } else {
      return defineClass(name, classData, 0, classData.length);
    }
  }
 
  private byte[] loadClassData(String className) {
    String fileName = root + File.separatorChar
        + className.replace('.', File.separatorChar) + ".class";
    try {
      InputStream ins = new FileInputStream(fileName);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      int bufferSize = 1024;
      byte[] buffer = new byte[bufferSize];
      int length = 0;
      while ((length = ins.read(buffer)) != -1) {
        baos.write(buffer, 0, length);
      }
      return baos.toByteArray();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }
 
  public String getRoot() {
    return root;
  }
 
  public void setRoot(String root) {
    this.root = root;
  }
 
  public static void main(String[] args) {
 
    MyClassLoader classLoader = new MyClassLoader();
    classLoader.setRoot("/Users/liuxp/tmp");
 
    Class<?> testClass = null;
    try {
      testClass = classLoader.loadClass("com.paddx.test.classloading.Test");
      Object object = testClass.newInstance();
      System.out.println(object.getClass().getClassLoader());
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }
  }
}
 

  運行上面的程序,輸出結(jié)果如下:

  

  自定義類加載器的核心在于對字節(jié)碼文件的獲取,如果是加密的字節(jié)碼則需要在該類中對文件進行解密。由于這里只是演示,我并未對class文件進行加密,因此沒有解密的過程。這里有幾點需要注意:

  1、這里傳遞的文件名需要是類的全限定性名稱,即com.paddx.test.classloading.Test格式的,因為 defineClass 方法是按這種格式進行處理的。

  2、最好不要重寫loadClass方法,因為這樣容易破壞雙親委托模式。

  3、這類 Test 類本身可以被 AppClassLoader 類加載,因此我們不能把 com/paddx/test/classloading/Test.class 放在類路徑下。否則,由于雙親委托機制的存在,會直接導致該類由 AppClassLoader 加載,而不會通過我們自定義類加載器來加載。

  四、總結(jié)

  雙親委派機制能很好地解決類加載的統(tǒng)一性問題。對一個 Class 對象來說,如果類加載器不同,即便是同一個字節(jié)碼文件,生成的 Class 對象也是不等的。也就是說,類加載器相當于 Class 對象的一個命名空間。雙親委派機制則保證了基類都由相同的類加載器加載,這樣就避免了同一個字節(jié)碼文件被多次加載生成不同的 Class 對象的問題。但雙親委派機制僅僅是Java 規(guī)范所推薦的一種實現(xiàn)方式,它并不是強制性的要求。近年來,很多熱部署的技術(shù)都已不遵循這一規(guī)則,如 OSGi 技術(shù)就采用了一種網(wǎng)狀的結(jié)構(gòu),而非雙親委派機制。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關文章

  • 淺談JVM垃圾回收之哪些對象可以被回收

    淺談JVM垃圾回收之哪些對象可以被回收

    這篇文章主要介紹了JVM垃圾回收之哪些對象可以被回收,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • Java通俗易懂系列設計模式之觀察者模式

    Java通俗易懂系列設計模式之觀察者模式

    這篇文章主要介紹了Java通俗易懂系列設計模式之觀察者模式,對設計模式感興趣的同學,一定要看一下
    2021-04-04
  • SpringBoot2.x 參數(shù)校驗問題小結(jié)

    SpringBoot2.x 參數(shù)校驗問題小結(jié)

    這篇文章主要介紹了SpringBoot2.x 參數(shù)校驗一些問題總結(jié),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • MyBatis詳細執(zhí)行流程的全紀錄

    MyBatis詳細執(zhí)行流程的全紀錄

    這篇文章主要給大家介紹了關于MyBatis詳細執(zhí)行流程的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • Java中EasyPoi導出復雜合并單元格的方法

    Java中EasyPoi導出復雜合并單元格的方法

    這篇文章主要介紹了Java中EasyPoi導出復雜合并單元格的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Mybatis實現(xiàn)傳入多個參數(shù)的四種方法詳細講解

    Mybatis實現(xiàn)傳入多個參數(shù)的四種方法詳細講解

    這篇文章主要介紹了Mybatis實現(xiàn)傳入多個參數(shù)的四種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-01-01
  • Java JSONObject與JSONArray對象案例詳解

    Java JSONObject與JSONArray對象案例詳解

    這篇文章主要介紹了Java JSONObject與JSONArray對象案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • 解決Servlet4.0版本使用注解設置url但無法訪問的問題

    解決Servlet4.0版本使用注解設置url但無法訪問的問題

    在學習servlet過程中,使用web.xml文件配置servlet可以正常訪問,但使用WebServlet注解時出現(xiàn)404錯誤,解決方法是在web.xml文件中將metadata-complete屬性改為false,啟動標注支持,然而該方法對我無效,最后通過重建項目和手動將新建的項目添加到tomcat服務器解決問題
    2024-10-10
  • Java中final與繼承操作實例分析

    Java中final與繼承操作實例分析

    這篇文章主要介紹了Java中final與繼承操作,結(jié)合實例形式分析了Java中使用final阻止繼承的相關原理與操作注意事項,需要的朋友可以參考下
    2019-09-09
  • Java CountDownLatch的源碼硬核解析

    Java CountDownLatch的源碼硬核解析

    對于并發(fā)執(zhí)行,Java中的CountDownLatch是一個重要的類。為了更好的理解CountDownLatch這個類,本文將通過例子和源碼帶領大家深入解析這個類的原理,感興趣的可以學習一下
    2022-10-10

最新評論