溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java類加載器的作用有哪些

發布時間:2020-12-07 15:08:59 來源:億速云 閱讀:347 作者:Leah 欄目:開發技術

Java類加載器的作用有哪些?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

Java的類加載器結構為下圖所示Java類加載器的作用有哪些

關于三層類加載器、雙親委派機制,本文不再板書,讀者可自行百度。

那么在JDK的源碼中,三層結構的具體實現是怎么樣的呢?

Bootstrap ClassLoader(引導類加載器)

引導類加載器是由C++實現的,并非Java代碼實現,所以在Java代碼中是無法獲取到該類加載器的。

一般大家都稱類加載器分為四種(引導類、擴展類、系統類以及用戶自定義的類加載器),但其實在JVM虛擬機規范中的支持兩種類型的類加載器,分別為引導類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader),所以擴展類和系統類也可以統稱為自定義類加載器。

Java類加載器的作用有哪些

Extension ClassLoader(擴展類加載器)和Appclass Loader(系統類加載器)

擴展類加載器和系統類加載器都是由Java語言編寫,具體實現為sum.misc.Launcher中的兩個內部類ExtClassLoader和AppClassLoader實現,我們進入到LaunchLacher這個類中看看(這個類在oracle jdk是沒有公開源碼的,需要看具體源碼的讀者可以下載open jdk中查看具體源碼,筆者這里就只是使用IDEA反編譯后生成的代碼進行解析):

首先是Laucncher的構造方法:

public Launcher() {
  Launcher.ExtClassLoader var1;
  try {
  	// 獲取擴展類加載器
   var1 = Launcher.ExtClassLoader.getExtClassLoader(); 
  } catch (IOException var10) {
   throw new InternalError("Could not create extension class loader", var10);
  }

  try {
  	// 獲取系統類加載器
   this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); 
  } catch (IOException var9) {
   throw new InternalError("Could not create application class loader", var9);
  }
		// 此處是將系統類加載器設置為當前線程的上下文加載器
  Thread.currentThread().setContextClassLoader(this.loader); 
  String var2 = System.getProperty("java.security.manager");
  if (var2 != null) {
   SecurityManager var3 = null;
   if (!"".equals(var2) && !"default".equals(var2)) {
    try {
     var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
    } catch (IllegalAccessException var5) {
    } catch (InstantiationException var6) {
    } catch (ClassNotFoundException var7) {
    } catch (ClassCastException var8) {
    }
   } else {
    var3 = new SecurityManager();
   }

   if (var3 == null) {
    throw new InternalError("Could not create SecurityManager: " + var2);
   }

   System.setSecurityManager(var3);
  }

 }

可以看到在Launcher的構造方法中定義了一個Launcher.ExtClassLoader類型的局部變量var1(這里是反編譯后的變量名),并調用Launcher.ExtClassLoader.getExtClassLoader()方法給該局部變量賦值,以及調用Launcher.AppClassLoader.getAppClassLoader(var1);給實例變量(類型為Launcher.AppClassLoader)賦值,需要注意的是,在給系統類加載器賦值時,將擴展類加載器作為參數傳入到了方法中。

同時,在構造方法中,將系統類加載器設置為了當前線程的上下文類加載器,關于上下文類加載器,主要用于基礎類型調用回用戶代碼時方法父類加載器區請求子類加載器完成類加載的行為,主要用于JDBC、JNDI等SPI服務提供者接口,這里不詳細展開。

上述源碼中的**getExtClassLoader()與getAppClassLoader()**方法源碼如下:

getExtClassLoader()是Launcher中的內部類ExtClassLoader(擴展類加載器)的一個靜態方法:

// 這是ExtClassLoader類內部的定義
private static volatile Launcher.ExtClassLoader instance;// 單例模式實例對象

public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
// 從這里可以看出,ExtClassLoader是一個由double-checking形成的懶漢式單例對象
   if (instance == null) { 
    Class var0 = Launcher.ExtClassLoader.class;
    synchronized(Launcher.ExtClassLoader.class) {
     if (instance == null) {
      instance = createExtClassLoader(); // 創建ExtClassLoader
     }
    }
   }

   return instance;
  }

// createExtClassLoader()方法
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
   try {
    return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
     public Launcher.ExtClassLoader run() throws IOException {
      File[] var1 = Launcher.ExtClassLoader.getExtDirs();
      int var2 = var1.length;

      for(int var3 = 0; var3 < var2; ++var3) {
       MetaIndex.registerDirectory(var1[var3]);
      }

      return new Launcher.ExtClassLoader(var1); // 調用構造方法
     }
    });
   } catch (PrivilegedActionException var1) {
    throw (IOException)var1.getException();
   }
  }

// ExtClassLoader的構造方法
public ExtClassLoader(File[] var1) throws IOException {
			// 此處第二個參數需要格外注意??!,我們進入父類的構造方法查看該參數是什么
   super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); 
   SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
  }

// 父類URLClassLoader的構造方法
// 此處的第二個參數是父類構造器的引用,也就解釋了為什么在調用獲得ExtClassLoader的
public URLClassLoader(URL[] urls, ClassLoader parent, getParent()方法獲取父類構造器為null
       URLStreamHandlerFactory factory) {
  super(parent);
  // this is to make the stack depth consistent with 1.1
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
   security.checkCreateClassLoader();
  }
  acc = AccessController.getContext();
  ucp = new URLClassPath(urls, factory, acc);
 }

getAppClassLoader()是Launcher中的內部類AppClassLoader(系統類加載器)的一個靜態方法:

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
   final String var1 = System.getProperty("java.class.path");
   final File[] var2 = var1 == null &#63; new File[0] : Launcher.getClassPath(var1);
   return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
    public Launcher.AppClassLoader run() {
     URL[] var1x = var1 == null &#63; new URL[0] : Launcher.pathToURLs(var2);
     return new Launcher.AppClassLoader(var1x, var0); // 與擴展類加載器不同的是,系統類加載器并不是單例模式的
    }
   });
  }
// AppClassLoader的構造方法
AppClassLoader(URL[] var1, ClassLoader var2) { 
 // 這里的var2 對應上述getAppClassLoader()方法中的var0,而var0對應的就是Launcher的構造方法中獲取到的ExtClassLoader
 // 在ExtClassLoader源碼的分析中,我們知道這個var2代表的就是父類構造器,所以此處就是將AppClassLoader的父類設置為ExtClassLoader
   super(var1, var2, Launcher.factory);
   this.ucp.initLookupCache(this);
  }

通過上述兩個方法,就可以解釋為什么在獲取擴展類加載器的父類時為null(即引導加載器),以及不同類加載器看似是繼承(Inheritance)關系,實際上是包含關系。在下層加載器中,包含著上層加載器的引用。

ClassLoader抽象類

上述的ExtClassLoader和AppClassLoader均繼承于ClassLoader類,ClassLoader抽象類也是類加載機制的基石,接下來我們就進入到該類中,看看它的一些主要方法。

public final classLoader getParent()

返回該類加載器的超類加載器

public Class<&#63;>loadclass(String name) throws ClassNotFoundException

加載名稱為name的類,返回結果為java.lang.Class類的實例。如果找不到類,則返ClassNotFoundException異常。該方法中的邏輯就是雙親委派模式的實現。

protected class<&#63;> findClass(string name)throws ClassNotFoundException

  • 查找二進制名稱為name的類,返回結果為java.lang.Class類的實例。這是一個受保護的方法,JVM鼓勵我們重寫此方法,需要自定義加載器遵循雙親委托機制,該方法會在檢查完父類加載器之后被loadClass()方法調用。
  • 在JDK1.2之前,在自定義類加載時,總會去繼承ClassLoader類并重寫loadClass方法,從而實現自定義的類加載類。但是在JDK1.2之后已不再建議用戶去覆蓋loadClass()方法,而是建議把自定義的類加載邏輯寫在findClass()方法中,從前面的分析可知, findClass()方法是在loadClass()方法中被調用的,當loadclass()方法中父加載器加載失敗后,則會調用自己的findClass()方法來完成類加載,這樣就可以保證自定義的類加載器也符合雙親委托模式。
  • 需要注意的是ClassLoader類中并沒有實現findClass()方法的具體代碼邏輯,取而代之的是拋出ClassNotFoundException異常,同時應該知道的是findClass方法通常是和defineClass方法一起使用的。一般情況下,在自定義類加載器時,會直接覆蓋ClassLoader的findClass()方法并編寫加載規則,取得要加載類的字節碼后轉換成流,然后調用defineClass()方法生成類的Class對象。
     

protected final Class<&#63;> defineClass(String name, byte[] b, int off,int len)

  • 根據給定的字節數組b轉換為Class的實例,off和len參數表示實際Class信息在byte數組中的位置和長度,其中byte數組b是ClassLoader從外部獲取的。這是受保護的方法,只有在自定義ClassLoader子類中可以使用。
  • defineClass()方法是用來將byte字節流解析郕VM能夠識別的cClass對象(ClassLoader中已實現該方法邏輯),通過這個方法不僅能夠通過class文件實例化class對象,也可以通過其他方式實例化class對象,如通過網絡接收一個類的字節碼,然后轉換為byte字節流創建對應的Class對象。
  • defineClass()方法通常與findClass()方法一起使用,一般情況下,在自定義類加載器時,會直接覆蓋ClassLoader的findClass()方法并編寫加載規則,取得要加載炎的字節碼后轉換成流,然后調用defineClass()方法生成類的Class對象。
     

protected final void resoiveClass(class<&#63;> c)

  • 鏈接指定的一個Java類。使用該方法可以使用類的Class對象創建完成的同時也被解析。前面我們說鏈接階段主要是對字節碼進行驗證,為類變量分配內存并設置初始值同時將字節碼文件中的符號引用轉換為直接引用。

protected final Class<&#63;> findLoadedClass(String name)

  • 查找名稱為name的已經被加載過的類,返回結果為java.lang.Class類的實例。這個方法是final方法,無法被修改。

private final ClassLoader parent;

  • 它也是一個ClassLoader的實例,這個字段所表示的ClassLoader也稱為這個ClassLoader的雙親。在類加載的過程中,classLoader可能會將某些請求交予自己的雙親處理。

關于這些方法,不一一展開,主要看一下loadClass()和findClass()。

loadClass()

public Class<&#63;> loadClass(String name) throws ClassNotFoundException {
		// loadClass調用重載含有兩個參數的loadClass,其中第二個參數表示在加載時是否解析,默認為false
  return loadClass(name, false); 
 }
// 含有兩個參數的重載loadClass方法
protected Class<&#63;> loadClass(String name, boolean resolve) throws ClassNotFoundException{// resolve:true->加載class的同時進行解析操作
  synchronized (getClassLoadingLock(name)) {// 同步操作,保證只能加載一次
   // 首先在緩存中判斷是否已經加載同名的類
   Class<&#63;> c = findLoadedClass(name);
   if (c == null) {
    long t0 = System.nanoTime();
    // 此處就是雙親委派機制的具體實現,其實就是讓父類加載器先去加載。
    try {
     // 獲取當前類加載器的父類加載器
     if (parent != null) {
      // 如果存在父類加載器,則調用父類加載器的loadClass進行加載(雙親委派)
      c = parent.loadClass(name, false);
     } else { 
     		 // parent == null:父類加載器是引導類加載器
      c = findBootstrapClassOrNull(name);
     }
    } catch (ClassNotFoundException e) {
     // ClassNotFoundException thrown if class not found
     // from the non-null parent class loader
    }
				// 當前類加載器的父類加載器未加載此類 or 當前類加載器未加載此類
    if (c == null) { 
     // If still not found, then invoke findClass in order
     // to find the class.
     long t1 = System.nanoTime();
     // 調用當前類加載器的findClass()
     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;
  }
 }

findClass()

//在ClassLoader中的findClass()方法
rotected Class<&#63;> findClass(String name) throws ClassNotFoundException {
  throw new ClassNotFoundException(name);
 }

可以看到,在ClassLoader中的findCLass()方法直接拋出異常,所以具體的實現是由子類進行重寫實現了;在ClassLoader的子類SecureClassLoader的子類URLClassLoader中對該方法進行了重寫。

URLClassLoader中的findCLass()方法

protected Class<&#63;> findClass(final String name)
  throws ClassNotFoundException
 {
  final Class<&#63;> result;
  try {
   result = AccessController.doPrivileged(
    new PrivilegedExceptionAction<Class<&#63;>>() {
     public Class<&#63;> run() throws ClassNotFoundException {
      String path = name.replace('.', '/').concat(".class");// 類名路徑字符串格式替換
      Resource res = ucp.getResource(path, false);// 獲得class源文件
      if (res != null) {
       try {
       	// 調用defineClass()方法獲得要加載的類對應的Class對象,
       	// defineClass()的作用就是根據給定的class源文件返回一個對應的Class對象
        return defineClass(name, res);
       } catch (IOException e) {
        throw new ClassNotFoundException(name, e);
       }
      } else {
       return null;
      }
     }
    }, acc);
  } catch (java.security.PrivilegedActionException pae) {
   throw (ClassNotFoundException) pae.getException();
  }
  if (result == null) {
   throw new ClassNotFoundException(name);
  }
  return result;
 }

最后補充一點關于數組類加載的細節

數組類的Class對象,不是由類加載器去創建的,而是在Java運行期JVM根據需要自動創建的。對于數組類的類加載器來說,是通過Class.getClassLoader()返回的,與數組當中元素類型的類加載器是一樣的,如果數組當中的元素類型是基本數據類型,數組類是沒有類加載器的。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女