jvm原理(16)類載入器實戰剖析與疑難點解析
三大類載入器所載入的路徑範圍:
public class MyTest18 {
public static void main(String[] args) {
System.out.println(System.getProperty("sun.boot.class.path"));//系統類載入器載入路徑
System.out.println(System.getProperty("java.ext.dirs"));//擴充套件類載入器載入路徑
System.out.println(System.getProperty("java.class.path" ));//應用類載入器載入過程
}
}
結果(根據自身環境不同會有所差異):
C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce .jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\classes
-------------------------------------------
C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
-------------------------------------------
C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;E:\Study\intelIde\jvm_lecture\out\production\classes;D:\IntelliJ IDEA 2017.2.4\lib\idea_rt.jar
下面我寫一個程式:
public class MyTest18_1 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
loader1.setPath("E:\\data\\classes\\");
Class<?> clazz = loader1.loadClass("com.twodragonlake.jvm.classloader.MyTest");
System.out.println("class : "+clazz.hashCode());
System.out.println("class loader :" + clazz.getClassLoader());
}
}
列印結果:
class : 1735600054
class loader :sun.misc.Launcher$AppClassLoader@18b4aac2
MyTest是由應用類載入器載入,這個沒啥毛病,那麼我們能不能想辦法讓啟動類載入器去載入MyTest呢?要想讓啟動類載入器載入MyTest必須讓啟動類載入器能夠找到MyTest的class檔案,那就需要把MyTest放到啟動類載入器的載入目錄裡邊。ok,那麼我們找到啟動類載入器的一個路徑比如【C:\Program Files\Java\jdk1.8.0_111\jre\classes】我們定位到此目錄下邊,此時你會發現C:\Program Files\Java\jdk1.8.0_111\jre\下邊並沒有classes資料夾,我們新建一個classes即可,既然sun.boot.class.path變數指定了這個目錄我們就能從裡邊嘗試載入類,然後我們把工程下邊的【com.twodragonlake.jvm.classloader.MyTest.class】放到C:\Program Files\Java\jdk1.8.0_111\jre\下邊,
OK,此時我們執行MyTest18_1 得到列印結果:
class : 2133927002
class loader :null
null表明是由啟動類載入器載入。即,MyTest.class是由啟動類載入器載入。做完實驗時候可以把classes目錄刪除,以免造成混繞。
接下來看一下擴充套件類載入器:
public class MyTest19 {
public static void main(String[] args) {
AESKeyGenerator aesKeyGenerator = new AESKeyGenerator();
System.out.println(aesKeyGenerator.getClass().getClassLoader());
System.out.println(MyTest19.class.getClassLoader());
}
}
列印結果:
sun.misc.Launcher$ExtClassLoader@12a3a380
sun.misc.Launcher$AppClassLoader@18b4aac2
那好我們能不能修改擴充套件類載入器的載入路徑java.ext.dirs的值,讓其從當前目錄載入如何?
顯然AESKeyGenerator無法被載入,因為當前目錄不存在這個類。
下一個例子:
public class MyTest20 {
public static void main(String[] args) throws Exception{
MyTest16 loader1 = new MyTest16("loader1") ;
MyTest16 loader2 = new MyTest16("loader2") ;
Class<?> clazz1 = loader1.loadClass("com.twodragonlake.jvm.classloader.MyPerson");
Class<?> clazz2 = loader2.loadClass("com.twodragonlake.jvm.classloader.MyPerson");
System.out.println(clazz1 == clazz2);
Object object1 = clazz1.newInstance();
Object object2 = clazz2.newInstance();
Method method = clazz1.getMethod("setMyPerson",Object.class);
method.invoke(object1,object2);
}
}
列印結果為true,原因是雖然loader1和loader2是2個不同的類載入器例項,但是他們都會委託應用類載入器去載入,如果應用類載入器載入過MyPerson,第二次不會再次載入。所以這2個載入器載入的類返回的結果是同一個類。
下邊的我們用clazz1建立的Method,然後呼叫的時候我們傳入的是clazz2的物件object2,由於object1和object2的class是同一個class,所以向下型別轉換不會出現問題。