对象创建过程之二(类加载器)
2019獨角獸企業重金招聘Python工程師標準>>>
JAVA為我們提供了兩種動態加載機制。
第一種是隱式機制。其實new一個對象和調用類的靜態方法時,就是隱式機制在工作。
第二種是顯示機制。顯示的機制又有兩種策略
第一種是用public static Class<?> forName(String className)。public static Class<?> forName(String name, boolean initialize, ClassLoader loader),第二種是用java.lang.ClassLoader的loadClass())。
Java程序啟動時,并不是一次把所有的類全部加載后再運行,它總是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載。
其中類加載過程:
1、尋找 jre 目錄,尋找jvm.dll,并初始化JVM;
2、產生一個Bootstrap Loader(啟動類加載器,用C++實現),在java虛擬機啟動的時候會利用這個類加載器來加載? JDK安裝目錄下的? /JRE/LIB/rt.jar等文件。? 通過System.getProperty("sun.boot.class.path")可以查詢。
3、Bootstrap Loader自動加載Extended Loader(標準擴展類加載器 ,用java實現),并將其父Loader設為Bootstrap Loader。這個ClassLoader是用來加載java的擴展API的,加載JDK安裝目錄下的/JRE/LIB/ext目錄中的類。可以通過System.getProperty("java.ext.dirs")進行查詢。
也可以指定搜索路徑: java -Djava.ext.dirs=d:/projects/testproj/classes HelloWorld
4、Bootstrap Loader自動加載AppClass Loader(系統類加載器 用java實現),并將其父Loader設為Extended Loader。這個ClassLoader是用來加載用戶機器上CLASSPATH設置目錄中的Class的。通過System.getProperty("java.class.path")可以查詢。也可以覆蓋環境變量: java -cp ./lavasoft/classes HelloWorld
5、最后由AppClass Loader加載HelloWorld類。
以上就是類加載的最一般的過程。
ExtClassLoader和AppClassLoader在JVM啟動后,會在JVM中保存一份,并且在程序運行中無法改變其搜索路徑。如果想在運行時從其他搜索路徑加載類,就要產生新的類加載器。
在JAVA中,一個類用其完全匹配類名(fully qualified class name)作為標識,這里指的完全匹配類名是包名和類名。不過在JVM中一個類是用其全名再附加上一個加載類ClassLoader的實例作為唯一標識。
同一個ClassLoader加載的類文件,只有一個Class實例。?
但是,如果同一個類文件被不同的ClassLoader載入,則會有兩份不同的ClassLoader實例(前提是著兩個類加載器不能用相同的父類加載器)
雙親委托模式:在任何一個自定義ClassLoader加載一個類之前,它都會先委托它的父親ClassLoader進行加載,只有當父親ClassLoader無法加載成功后,才會由自己加載。
《特例是線程上下文類加載器,使用線程上下文類加載器, 可以在執行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類加載器加載類.
典型的例子有, 通過線程上下文來加載第三方庫jndi實現, 而不依賴于雙親委派. 大部分java app服務器(jboss, tomcat..)也是采用contextClassLoader來處理web服務。
以 Apache Tomcat 來說,每個 Web 應用都有一個對應的類加載器實例。該類加載器也使用代理模式,所不同的是它是首先嘗試去加載某個類,如果找不到再代理給父類加載器。這與一般類加載器的順 序是相反的。這是 Java Servlet 規范中的推薦做法,其目的是使得 Web 應用自己的類的優先級高于 Web 容器提供的類。
這種代理模式的一個例外是:Java 核心庫的類是不在查找范圍之內的。這也是為了保證 Java 核心庫的類型安全。 》
在JVM加載類的時候,需要經過三個步驟,裝載、連接、初始化。裝載就是找到相應的class文件,讀入JVM,初始化就不用說了,最主要就說說連接。
forName加載的時候就會將Class進行解釋和初始化。forName也有另外一個版本的方法,可以設置是否初始化以及設置ClassLoader,在此就不多講了。 loadClass加載類實際上就是加載的時候并不對該類進行解釋,因此也不會初始化該類。
?
加載過程中會先檢查類是否被已加載,檢查順序是自底向上,而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
卸載重載:一個已經加載的類是無法被更新的,如果你試圖用同一個ClassLoader再次加載同一個類,就會得到異常(java.lang.LinkageError: duplicate classdefinition),我們只能夠重新創建一個新的ClassLoader實例來再次加載新類。至于原來已經加載的類,開發人員不必去管它,因為它可能還有實例正在被使用,只要相關的實例都被內存回收了,那么JVM就會在適當的時候把不會再使用的類卸載。
真正完成類的加載工作是通過調用 defineClass來實現的;而啟動類的加載過程是通過調用 loadClass來實現的。前者稱為一個類的定義加載器(defining loader),后者稱為初始加載器(initiating loader)。在 Java 虛擬機判斷兩個類是否相同的時候,使用的是類的定義加載器。
《注意事項:如果A是由Bootstrap Loader所載入,這個時候,要加入B,先交給parent進行查詢,這時parent為null,交給自己查詢,自己又沒有,就報錯。》
?
線程上下文類加載器
線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的。類?java.lang.Thread?中的方法getContextClassLoader()?和?setContextClassLoader(ClassLoader cl)?用來獲取和設置線程的上下文類加載器。如果沒有通過?setContextClassLoader(ClassLoader cl)?方法進行設置的話,線程將繼承其父線程的上下文類加載器。Java 應用運行的初始線程的上下文類加載器是系統類加載器。在線程中運行的代碼可以通過此類加載器來加載類和資源。
?前面提到的類加載器的代理模式并不能解決 Java 應用開發中會遇到的類加載器的全部問題。Java 提供了很多服務提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實現。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定義包含在javax.xml.parsers?包中。這些 SPI 的實現代碼很可能是作為 Java 應用所依賴的 jar 包被包含進來,可以通過類路徑(CLASSPATH)來找到,如實現了 JAXP SPI 的?Apache Xerces?所包含的 jar 包。SPI 接口中的代碼經常需要加載具體的實現類。如 JAXP 中的?javax.xml.parsers.DocumentBuilderFactory?類中的newInstance()?方法用來生成一個新的?DocumentBuilderFactory?的實例。這里的實例的真正的類是繼承自javax.xml.parsers.DocumentBuilderFactory?, 由 SPI 的實現所提供的。如在 Apache Xerces 中,實現的類是?org.apache.xerces.jaxp.DocumentBuilderFactoryImpl?。 而問題在于,SPI 的接口是 Java 核心庫的一部分,是由引導類加載器來加載的;SPI 實現的 Java 類一般是由系統類加載器來加載的。引導類加載器是無法找到 SPI 的實現類的,因為它只加載 Java 的核心庫。它也不能代理給系統類加載器,因為它是系統類加載器的祖先類加載器。也就是說,類加載器的代理模式無法解決這個問題。
?線程上下文類加載器正好解決了這個問題。如果不做任何的設置,Java 應用的線程的上下文類加載器默認就是系統上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實現的類。線程上下文類加載器在很多 SPI 的實現中都會用到.
轉載于:https://my.oschina.net/u/1182621/blog/146145
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的对象创建过程之二(类加载器)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: putty以及psftp的基本操作,使用
- 下一篇: 微信定时自动发送群消息的小工具-pyth