歡迎裝載請說明出處:http://blog.csdn.net/yfqnihao
課程源碼:http://download.csdn.net/detail/yfqnihao/4866501
前面第三和第四節我們一直在強調一句話,類裝載器和安全管理器是可以被動态擴充的,或者說,他們是可以由使用者自己定制的,今天我們就是動手試試,怎麼做這部分的實踐,當然,在閱讀本篇之前,至少要閱讀過筆記三。
下面我們先來動态擴充一個類裝載器,當然這隻是一個比較小的demo,旨在讓大家有個比較形象的概念。
第一步,首先定義自己的類裝載器,從ClassLoader繼承,重寫它的findClass方法,至于為什麼要這麼做,大家如果看過筆記三就知道,雙親委托模式下,如果parent沒辦法loadClass,bootStrap也沒把辦法loadClass的時候,jvm是會調用ClassLoader對象或者它子類對象的findClass來裝載。
package com.yfq.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = getByteArray(name);
if (data == null) {
throw new ClassNotFoundException();
}
return defineClass(name, data, 0, data.length);
}
private byte[] getByteArray(String name){
String filePath = name.replace(".", File.separator);
byte[] buf = null;
try {
FileInputStream in = new FileInputStream(filePath);
buf = new byte[in.available()];
in.read(buf);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buf;
}
}
第二步,定義一個類,專門用于被裝載,這裡我們定義了一個靜态代碼塊,待會用到它
package com.yfq.test;
public class TestBeLoader {
static{
System.out.println("TestBeLoader init");
}
public void sayHello(){
System.out.println("hello");
}
}
第三步,定義一個有main函數入口的public類來做驗證
package com.yfq.test;
public class TestClassLoaderDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Class thisCls = TestClassLoaderDemo.class;
MyClassLoader myClassLoader = new MyClassLoader();
System.out.println(thisCls.getClassLoader());
System.out.println(myClassLoader.getParent());
try {
//用自定義的類裝載器來裝載類,這是動态擴充的一種途徑
Class cls2 = myClassLoader.loadClass("com.yfq.test.TestBeLoader");
System.out.println(cls2.getClassLoader());
TestBeLoader test=(TestBeLoader)cls2.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
第四步,檢視運作結果
sun.misc.Launcher$AppClassLoader@19821f
TestBeLoader init
說明:
第一個輸出:裝載TestClassLoaderDemo的類是AppClassLoder
第二個輸出:裝載myClassLoader的裝載器也是AppClassLoader,這裡也驗證了我們筆記三講的,在同個線程中,動态連接配接模式會運用目前線程的類加載器來加載所需的class檔案,因為第一個和第二個輸出是同一個對象的對象名
第三個輸出:是TestBeLoader的類加載器,這個輸出驗證了,雙親委托模式下的動态連接配接模式,由于myClassLoader是由AppClassLoader裝載的,是以它會委托自己的parent來裝載com.yfq.test.TestBeLoader這個類,加載成功是以就不再調用自己的findClass方法,這個我們在筆記三有做過簡要的讨論。
第四個輸出:如果我們将TestBeLoader test=(TestBeLoader)cls2.newInstance();這句話注掉,則不會有第四個輸出,為什麼?
類的裝載大緻分為三步,裝載,連接配接,初始化。而初始化這一步,是在我們第一次建立對象的時候才進行初始化配置設定記憶體,這一點需要注意,并不是class被load記憶體後就立刻初始化。