JVM 預定義的三種類型類加載器:
類型 | 實現 | 負責加載類庫的路徑 | 開發者 |
---|---|---|---|
啟動類 加載器 |
本地代碼 |
jre/lib 下面的類庫,如 rt.jar
|
無法操作 |
擴展類 加載器 |
ExtClassLoader |
jre/lib/ext 下面的類庫 或者 系統變量 java.ext.dir 指定位置中的類庫 |
可以操作 |
系統類 加載器 |
AppClassLoader | 系統類路徑(CLASSPATH)中指定的類庫 | 可以操作 |
注:我們平時自己定義的類就是在 CLASSPATH 路徑指定的系統類。
雙親委派機制
某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。
啟動類 - father - 擴展類 - father - 系統類
實例
src 下定義 HelloWorld.java
csu/src/HelloWorld.java
public class HelloWorld {
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
命令行運行 生成 字節碼文件 HelloWorld.class
ShuaideMacBook-Pro:src shuai$ javac HelloWorld.java
ShuaideMacBook-Pro:src shuai$ ls
HelloWorld.class HelloWorld.java
運行字節碼文件 輸出 HelloWorld
ShuaideMacBook-Pro:src shuai$ java HelloWorld
HelloWorld
然后將字節碼文件打成 jar 包,命名為 h.jar
ShuaideMacBook-Pro:src shuai$ jar -cvf h.jar HelloWorld.class
已添加清單
正在添加: HelloWorld.class(輸入 = 419) (輸出 = 285)(壓縮了 31%)
再將 h.jar 移到 擴展類加載器 負責的類庫路徑下 jre/lib/ext
在這之后,改變 HelloWorld.java
public class HelloWorld {
public static void main(String[] args){
System.out.println("輸出內容更改");
}
}
并重新編譯執行
ShuaideMacBook-Pro:src shuai$ javac HelloWorld.java
ShuaideMacBook-Pro:src shuai$ java HelloWorld
HelloWorld
因為 Java 的雙親委派機制, 擴展類加載器下有相應的 jar 包,所以輸出內容不變。
面試題
能不能自己寫個類叫 java.lang.System ?
答案:通常不可以,但可以采取另類方法達到這個需求。
解釋:為了不讓我們寫System類,類加載采用委托機制,這樣可以保證爸爸們優先,爸爸們能找到的類,兒子就沒有機會加載。而System類是Bootstrap加載器加載的,就算自己重寫,也總是使用Java系統提供的System,自己寫的System類根本沒有機會得到加載。
但是,我們可以自己定義一個類加載器來達到這個目的,為了避免雙親委托機制,這個類加載器也必須是特殊的。由于系統自帶的三個類加載器都加載特定目錄下的類,如果我們自己的類加載器放在一個特殊的目錄,那么系統的加載器就無法加載,也就是最終還是由我們自己的加載器加載。