為深入了解 Java ClassLoader 機制,先做實驗:
package java.lang;
public class Test {
public static void main(String[] args) {
char[] c = "1234567890".toCharArray();
String s = new String(0, 10, c);
}
}
String 類有一個 package 權限的構造函數 String(int offset, int length, char[] array),按預設訪問權限,Test 屬於 java.lang 包,理論上可訪問。編譯通過,但運行拋出 SecurityException: Prohibited package name: java.lang。要弄清原因,必須搞懂 ClassLoader 機制。
Java ClassLoader 動態加載 class,對一個 class 只加載一次。JVM 使用的 ClassLoader 共 4 種,優先級由高到低:
採用「雙親委派模型」:若網絡類加載器被請求加載 java.lang.Integer,會先將請求發送給上一級類路徑加載器;若上一級返回已加載,則網絡類加載器不再加載;否則才自行加載。類似地,類路徑加載器收到請求後也會先委派給標準擴展類加載器,以此類推。啟動類加載器優先級最高,若它按自己方式找到 java.lang.Integer,下層 ClassLoader 都無法再加載該類,即使你自己寫了一個 java.lang.Integer 也無法取代核心庫的類,因根本無法被下層加載器加載。
Java 語言規定:同一包中無修飾符默為 package 權限,包內類可訪問。但這不夠準確。確切說,只有由同一個 ClassLoader 加載的類才擁有上述 package 權限。例如啟動類加載器加載了 java.lang.String,類路徑加載器加載了我們寫的 java.lang.Test,它們無法互相訪問對方的 package 權限方法。這就阻止了惡意代碼訪問核心類的 package 權限方法。