天天看點

java jvm學習筆記五(實踐自己寫的類裝載器)

         歡迎裝載請說明出處: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記憶體後就立刻初始化。