ClassLoader雙親委派模式作用詳解
前言
我們的面試中經常會遇到關於ClassLoader的問題,但是我們的日常開發中又沒有直接編寫過ClassLoader相關的代碼。對於小白新手來說,可能都不知道ClassLoader是用來幹嘛的,它是如何在無形當中影響我們編寫的代碼的?
ClassLoader的作用
見名知意,ClassLoader就是類加載器,它的作用就是將我們編寫的java代碼加載到JVM虛擬機中。在JVM啟動的時候是不會一次性把所有的java類加載進去的,而是在需要的時候才加載指定的類文件,要不然類特別多的話,大部分類一時用不上,那就浪費內存資源瞭。既然ClassLoader是用來加載類文件的,那麼我們平時寫的java代碼是如何加載的呢?
ClassLoader的種類
在JDK中,默認是有三種ClassLoader的:
Bootstrap ClassLoader
主要加載核心類庫,加載${JRE_HOME}/lib下的rt.jar、resources.jar等;
Extension ClassLoader
加載擴展類庫,加載${JRE_HOME}/lib/ext文件夾下的jar包和class文件;
另外還會加載-D java.ext.dirs
指定的目錄下的jar包和class文件;
AppClassLoader
加載當前應用classpath下的所有class文件;
如何實現雙親委派模式
在Launcher
類中,我們可以看到Launcher
創建的時候,同時創建瞭ExrClassLoader
與AppClassLoader
對象。
sun.misc.Launcher
:
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); }
1.創建ExtClassLoader對象;
2.創建AppClassLoader對象,並把ExtClassLoader對象作為AppClassLoader的父級ClassLoader;
3.把AppClassLoader對象綁定到線程上下文中;
為什麼沒有提到BoostrapClassLoader?
因為BoostrapClassLoader在java層面是拿不到的,ExtClassLoader的父級ClassLoader就是BoostrapClassLoader,java層面取出來就是null;
為瞭瞭解清楚類的加載方式,我們首先需要從AppClassLoader
中的loadClass()
方法中入手:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先, 檢查這個類是否已經加載好瞭 Class<?> c = findLoadedClass(name); // 如果沒有加載過 if (c == null) { long t0 = System.nanoTime(); try { // 如果父級ClassLoader不為空,那麼就先嘗試讓父級ClassLoader加載 if (parent != null) { c = parent.loadClass(name, false); } else { // 如果父級ClassLoader為空,有可能父級ClassLoader是BootstrapClassLoader,那麼先嘗試在BootstrapClassLoader加載 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // 如果一直向上都沒有加載目標class,那麼最終回到當前ClassLoader加載 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; } }
通過以上代碼分析,我們可以大概瞭解到雙親委派模式瞭:
1.先在當前ClassLoader檢查是否已經加載瞭目標類;
2.如果當前ClassLoader沒有加載目標類,那麼先向嘗試讓父級ClassLoader加載目標類,直至BootstrapClassLoader;
3.如果最終所有的父級ClassLoader都沒有加載目標類,那麼當前ClassLoader嘗試自己加載目標類;
4.所有父級ClassLoader重復操作1~3步驟;
5.隻要其中任意一個ClassLoader成功加載目標類,那麼就直接返回;
小測試
為瞭驗證小夥伴們是否已經明白瞭雙親委派模式,我們出一個小小的測試題留給大傢:
我們通過自己創建一個java.lang.String的類(類名和包名和JDK中的String.class一致),這個自定義的String類能不能通過AppClassLoader成功地加載到JVM中?
以上就是ClassLoader雙親委派模式作用詳解的詳細內容,更多關於ClassLoader雙親委派模式的資料請關註WalkonNet其它相關文章!