詳細內容請參考原鏈接:http://blog.csdn.net/jiangwei0910410003/article/details/17733153,此文只是轉載
1、新建一個java對象,JVM需要把這個對象對應的字節碼加載到內存中,字節碼就是對應的Class文件,自己java工程,其在bin目錄下,也就是把.class文件的內容讀到內存中;
2、Java虛擬機中類加載器
java虛擬機中可以安裝多個類加載器,系統默認三個主要的類加載器,每個類負責加載特定位置的類:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++寫的,內置在JVM內核中,也就是所有類的鼻祖;
之所以有不同的類加載器,是因爲不同的類加載器加載不同目錄下的.class文件;採用委託的方式,向父類請求加載,每個父類去指定的目錄加載,成功結束;不成功再順序往下請求,直到加載成功,所有加載類都沒有成功,則拋出ClassNotFoundException異常
類加載器的委託機制:
當Java虛擬機要加載第一個類的時候,到底派出哪個類加載器去加載呢?
(1). 首先當前線程的類加載器去加載線程中的第一個類(當前線程的類加載器:Thread類中有一個get/setContextClassLoader(ClassLoader cl);方法,可以獲取/指定本線程中的類加載器)
(2). 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器來加載類B
(3). 還可以直接調用ClassLoader.loadClass(String className)方法來指定某個類加載器去加載某個類
每個類加載器加載類時,又先委託給其上級類加載器當所有祖宗類加載器沒有加載到類,回到發起者類加載器,還加載不了,則會拋出ClassNotFoundException,不是再去找發起者類加載器的兒子,因爲沒有getChild()方法。例如:如上圖所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定義的MyClassLoader1首先會先委託給AppClassLoader,AppClassLoader會委託給ExtClassLoader,ExtClassLoader會委託給BootStrap,這時候BootStrap就去加載,如果加載成功,就結束了。如果加載失敗,就交給ExtClassLoader去加載,如果ExtClassLoader加載成功了,就結束了,如果加載失敗就交給AppClassLoader加載,如果加載成功,就結束了,如果加載失敗,就交給自定義的MyClassLoader1類加載器加載,如果加載失敗,就報ClassNotFoundException異常,結束;
因爲BootStrap不是java類,所以打印出來的class是null
3、System類、list、map這樣的類都在rt.jar中,需要由BootStrap來加載,所以其加載類打印出來爲null;也就是說,誰加載的你當前這個jar,那麼它的classloader就是誰,比方說把MyClassLoader打包成jar,然後放到jre1.x/lib/ext目錄下,那麼它的第一個加載類就會變成sun.misc.Launcher$ExtClassLoader
4、可以定義自己的類加載器,需要將自己的類加載器掛載到系統類加載器樹上,在構造函數時可以指定類加載器
5、定義了自己的類加載器,每個加載器都有自己的搜索目錄,自己類加載器有自己的目錄,而它的父類也有自己的目錄,所以類名字,如果父類找到了,那麼子類不會繼續查找了;所以即使使用了自己的類加載器,如果是父類找到了對應名稱的類class,其加載器就是父類
編寫自己的加載類:
先寫一個測試類:
public class ClassLoaderAttachment extends Date{
private static final long serialVersionUID = 98398492342L;
@Override
public String toString() {
// TODO Auto-generated method stub
return "Hell ClassLoaderAttachment";
}
}
編譯後,在bin目錄會生成它的.class文件,然後在項目目錄(bin的上一層把)創建一個”temp_folder”目錄,用於生成加密後的.class文件
寫一個main函數,把上面的ClassLoaderAttachment.class文件加密
//測試,先將ClassLoaderAttachment.class文件加密寫到工程的class_temp目錄下
public static void main(String[] args) throws Exception{
//配置運行參數
String srcPath = args[0];//ClassLoaderAttachment.class原路徑
String desPath = args[1];//ClassLoaderAttachment.class輸出的路徑
String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String desPathFile = desPath + "/" + desFileName;
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(desPathFile);
//將class進行加密
encodeAndDecode(fis,fos);
fis.close();
fos.close();
}
/**
* 加密和解密算法
* @param is
* @param os
* @throws Exception
*/
private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{
int bytes = -1;
while((bytes = is.read())!= -1){
bytes = bytes ^ 0xff;//和0xff進行異或處理
os.write(bytes);
}
}
會在temp_folder目錄下生成一個新的ClassLoaderAttachment.class,此目錄和class文件名用於創建自己的ClassLoader時,作爲參數傳入
下面編寫自己的加載類
public class myLoadClass extends ClassLoader{
//需要加載類.class文件的目錄
private String classDir;
//無參的構造方法,用於class.newInstance()構造對象使用
public myLoadClass(){
}
public myLoadClass(String classDir){
this.classDir = classDir;
}
@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//class文件的路徑
String classPathFile = classDir + "/" + name + ".class";
try {
//將class文件進行解密
FileInputStream fis = new FileInputStream(classPathFile);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
encodeAndDecode(fis,bos);
byte[] classByte = bos.toByteArray();
//將字節流變成一個class
return defineClass(classByte,0,classByte.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
//測試,先將ClassLoaderAttachment.class文件加密寫到工程的class_temp目錄下
public static void main(String[] args) throws Exception{
//配置運行參數
String srcPath = args[0];//ClassLoaderAttachment.class原路徑
String desPath = args[1];//ClassLoaderAttachment.class輸出的路徑
String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String desPathFile = desPath + "/" + desFileName;
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(desPathFile);
//將class進行加密
encodeAndDecode(fis,fos);
fis.close();
fos.close();
}
/**
* 加密和解密算法
* @param is
* @param os
* @throws Exception
*/
private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{
int bytes = -1;
while((bytes = is.read())!= -1){
bytes = bytes ^ 0xff;//和0xff進行異或處理
os.write(bytes);
}
}
}
其中的main函數就是剛纔加密class文件用的
然後寫測試myDexLoader的測試方法:
public class myMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//輸出ClassLoaderText的類加載器名稱
System.out.println("ClassLoaderText類的加載器的名稱:"+myMain.class.getClassLoader().getClass().getName());
System.out.println("System類的加載器的名稱:"+System.class.getClassLoader());
System.out.println("List類的加載器的名稱:"+List.class.getClassLoader());
ClassLoader cl = myMain.class.getClassLoader();
while(cl != null){
System.out.print(cl.getClass().getName()+"->");
cl = cl.getParent();
}
System.out.println(cl);
/**
* 如果在bin目錄下有ClassLoaderAttachment.class,那麼用的是系統的appClassLader,因爲父類已經找到了
* 如果想自己的類加載,那麼需要把bin目錄下對應的.class文件刪掉,使父類找不到,這樣自己的加載類纔有機會去執行
* **/
Class myClass = new myLoadClass("temp_folder").loadClass("ClassLoaderAttachment");
Date myDate = (Date)myClass.newInstance();
System.out.println("classLoader is :"+myDate.getClass().getClassLoader().getClass().getName());
System.out.println("date result is "+myDate.toString());
ClassLoader cll = myDate.getClass().getClassLoader();
while(cll != null){
System.out.print(cll.getClass().getName()+"->");
cll = cll.getParent();
}
System.out.println(cll);
}
}
也是一個main方法,執行即可;把bin目錄下的ClassLoaderAttachment.class刪除後,最後得到的加載類鏈表是:
由於temp_folder目錄下是加了密的class文件,所以只能被我們自己能解密的ClassLoader進行加載執行,其實就是返回真是的字節碼;
如果把它拷貝到bin目錄,那麼系統的sun.misc.Launcher$AppClassLoader會進行加載此class,但是文件是加了密的,所以其解析錯誤,則會報執行錯誤