c 自定义实现string类 clear_有关类加载器的总结
對于java開發來說,classLoader往往是容易被我們忽視的一個重要知識點。而classLoader對java的發展也有很大的影響。例如。
學習知識往往帶著疑問去學習,效果可能會比較好。接下來我帶著幾個問題來解答classLoader有關的疑問。
接下來就一一解答上面的疑問。以我的學習方式來說,對這塊知識點用反證的思維來理解比較簡單。
分析classLoader之前,我們先直接class的加載過程,并且下面方法跟加載過程一一對應。
類加載過程 , copy from http://www.importnew.com/25295.html- 加載class文件,從jvm中對應的Class文件。
- 接著驗證Class是否合法。即對class進行有效性、合法性驗證。
- 準備階段的工作:為類變量分配內存并初始化值,這里的初初始化值并不是設置用戶指定的值。例如。
public static String name = "wang007";
初始化值,先將int = 0; name = null;在接下來的初始化階段, 再講 int = 12, name = "wang007";
其他階段可參考JVM 類加載機制詳解 - ImportNew;
1. 什么是classLoader?為什么需要classLoader?
classLoader是用于加載我們編譯好的class文件到jvm中。在ClassLoader這個類中能看到相關重要的方法。下面我就列舉幾個比較重要的方法來解釋一下。
publicloadClass方法,根據 java全限定名稱 加載 很明顯,該方法對應類加載過程的 加載階段
findClass方法, 該方法是需要子類去實現的方法, 根據 java全限定名稱 加載 class文件。 可以從本地文件系統加載, 也可以通過網絡從任一服務器中加載。 對于需要自實現classLoader的話,一般只需要實現該方法即可。
resolveClass方法,根據loadClass方法加載好的Class對象, 進行連接工作。 即驗證、準備、解析方法。
還有defineClass方法,將用byte[]二進制數組 創建class對象。final方法無法拓展,在findClass方法中,獲取byte[],調用defineClass方法創建Class對象。
2 什么是雙親委派? 為什么需要雙親委派?
說到雙親委派模型,那么必須先介紹BootstrapClassLoader,ExtClassLoader,AppClassLoader.
- BootstrapClassLoader負責加載rt.jar包中的class文件。 C++寫的,所以在java不可見,用null引用代替。
- ExtClassLoader負責加載ext目錄的jar包中的class文件。
- AppClassLoader負責加載classpath下的class文件,即我們編寫的java文件編譯成的class文件。
所謂的雙親委派模型是指加載class時, 先讓父classLoader加載class,如果父classLoader加載不到,再自己加載。 這里的“父”, 不是指繼承關系,而是指屬性指定的“父”ClassLoader。
AppClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器是BootstrapClassLoader。
public 在ClassLoader這個類中, 用parent屬性指定當前classLoader的“父”加載器。 AppClassLoader,ExtClassLoader都是繼承該類。為什么需要雙親委派?
雙親委派模型是為了保證應用安全的,確保rt.jar包內的代碼只由BootstrapClassLoader加載的。 例如我們在代碼中用AppClassLoader(即SystemClassLoader)加載String.class,AppClassLoader會讓ExtClassLoader加載的, ExtClassLoader會讓BootClassLoader加載,因為String.class在rt.jar中,所以BootstrapClassLoader會加載成功, 返回到 ExtClassLoader, 最后返回到AppClassLoader中。
如果我們用自實現的ClassLoader加載String.class(這個String.class我加點后門代碼),不通過雙親委派模型,直接自己加載。是可以加載出來的,但是跟BootstrapClassLoader加載出來的String不是同一個。因為ClassLoader不同, 加載出來的Class對象也不同。 如果讓自定義ClassLoader加載出來的String 賦值給BootClassLoader,會報ClassCastException.
這里展示雙親委派的關鍵代碼
//這里指定 resolve = false,所以ClassLoader加載class是不會導致類的初始化的有疑惑點的地方,再詳情說說。
3. 什么是contextClassLoader? 為什么需要ContextClassLoader?
ContextClassLoader就是通過thread.getContextClassLoader()方法獲取的ClassLoader。 我知道,如果我換做你,聽到這種解釋, 我都想錘死我自己。哈哈哈。
上面說的雙親委派模型, 看似天衣無縫,其實未必。我覺得ContextClassLoader恰恰是為雙親委派模型擦屁股的角色。
當我們需要加載一個Class的對象,從自定義ClassLoader,到appClassLoader,再到ExtClassLoader,最后到BootstrapClassLoader。沒問題, 很順利。這是從下到上加載。 但是反過來,當從上到下加載的時候,這個變得是一個不可能完成的任務。為了彌補這個缺陷, 特定設計的ContextClassLoader。
但可能又會有疑問了, 什么情況下, 會出現從上到下加載呢?
我舉個很實用,但是又幾乎用不到的例子吧(是不是像極了好吃又吃不飽的東西。233333) spi機制。 這里我就不展開講spi了。有興趣的話,自行搜索資料學習,基本大部分框架都會用到spi。
我們都知道,SUN是個一流的公司,因為一流的公司賣標準嘛, 雖然這個一流的公司已經死了。 在rt.jar包中, 定義了很多spi接口, JDBC,JNDI等等。 但是spi接口的實現一般都是以第三方jar的方式提供到classpath下。 例如, JDBC spi的接口由BootstrapClassLoader加載, 但是有些spi接口中會加載spi實現包的類,而BootstrapClassLoader只能加載rt.jar的類, 不能classPath下的類,所以通過thread.getContextClassLoader方法獲取到ClassLoader(這個一般是AppClassLoader)。然后通過這個classLoader在spi 接口中加載其實現。
補充一個知識點。如果類A引用類B,且類B還未加載, 那么用加載類A的類加載器加載類B。
我找了下,只找到了DriverManger這個類下,通過spi機制加載Driver實現。(例如mysql driver)只不過這個用的是ClassLoader.getSystemClassLoader()方法, 效果跟contextClassLoader差不多。都是為了加載第三方實現類。
public接下來來看下。ContextClassLoader是怎么來的。
public在Launcher的構造方法中,獲取ExtClassLoader,如果是第一次調用,就會創建一個ExtClassLoader。 獲取再獲取AppClassLoader。第一次調用, 也會創建一個。 最后將appClassLoader設置到Thread。
我們知道,main方法是java程序的入口,而main方法所在的類也是有AppClassLoader加載的。 而AppClassLoader正在來自于Launcher類,所以在Launcher類在main方法運行之前就創建好了。
為啥AppClassLoader也叫SystemClassLoader呢? 因為:
public在ClassLoader類中的getSystemClassLoader方法,就是從launcher類中獲取的appClassLoader,并設置到ClassLoader類的靜態屬性中。 所以AppClassLoader也叫SystemClassLoader
ContextClassLoader的傳播。
//thread的構造方法中調用init方法在新建一個線程的時候,就是會把原來線程所在的ClassLoader設置到新的線程中。 就這么一直傳播下去。 如果我們改變當前線程的ContextClassLoader,那么當前線程上新建Thread的ContextClassLoader就是改變后的。
例如。
public所以ContextClassLoader的傳播是通過新建Thread的時候,把當前線程的ContextClassLoader設置到新線程中。
同時還有類似方式傳播的東西。inheritableThreadLocals。 也叫可繼承的ThreadLocal。有興趣的話,可以自行查找資料。InheritableThreadLocal。跟ThreadLocal差不多的作用。
4. ClassLoader的運用場景。
其實ClassLoader,我們直接或間接的用了很多。例如Tomcat需要加載項目中的class文件。 按照默認提供的ClassLoader是不夠的。必須自定義ClassLoader來加載項目中的class文件。還有熱啟動機制,想必也是用到了自定義ClassLoader。
最后再解決一個疑問點。 Class.forName與ClassLoader.loadClass 的區別?
前面介紹了ClassLoader的loadClass方法,是不會觸發類的初始化操作的。
而Class.forName是會觸發類的初始化操作。
public在forName0方法中,第一個參數就是類名,第二個參數是是否初始化類, 第三個參數是指定ClassLoader。 所以在forName方法中, 指定了需要初始化, 還指定了ClassLoader為當前調用類的ClassLoader。
其實Class.forName還有一個重載方法,可以指定是否初始化,和ClassLoader
public好了,有關classLoader的知識就總結到這里了。 有些點由于篇幅關系就沒有深入下去了。
總結
以上是生活随笔為你收集整理的c 自定义实现string类 clear_有关类加载器的总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 读取txt文件为字典_py
- 下一篇: tensorboard ckpt pb