深入Java日記——類加載器

類加載器的作用

  1. 通過一個類的全限定名稱來獲取此類的二進制字節(jié)流,并加載到內(nèi)存中(需要使用類加載器)
  2. 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)
  3. 在堆中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口

類緩存

標準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(緩存)一段時間。不過,JVM垃圾收集器可以回收這些Class對象

類加載器的層級結(jié)構(gòu)(樹狀結(jié)構(gòu))

1.引導類加載器(Bootstrap ClassLoader)

  • 它用來加載Java的核心庫(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.Path路徑下的內(nèi)容),是用原生代碼來實現(xiàn)的,并不繼承自java.lang.classloader。

  • 加載擴展類和應用程序類加載器,并指定他們的父類加載器。

  • 啟動類加載器無法被Java程序直接引用

2.擴展類加載器(Extension ClassLoader)

  • 用來加載Java的擴展庫(JAVA_HOME/jre/ext/*.jar或java.ext.dirs路徑下的內(nèi)容)。 Java虛擬機的實現(xiàn)會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載Java類。
  • 由sun.misc.Launcher$ExtClassLoader實現(xiàn)。

**3.應用程序類加載器(Application ClassLoader) **

  • 它根據(jù)Java應用的類路徑(classpath,java.class.path類。 一般來說,Java應用的類都是由它來完成加載的。
  • 由sun.misc.Launcher$AppClassLoader實現(xiàn)。

4.自定義類加載器

  • 開發(fā)人員可以用過繼承java.lang.ClassLoader類的方式實現(xiàn)自己的類加載器,以滿足一些特殊的要求

雙親委派模式

雙親委派模型

從JDK1.2開始,java虛擬機規(guī)范推薦開發(fā)者使用雙親委派模式(ParentsDelegation Model)進行類加載,其加載過程如下:

  1. 如果一個類加載器收到了類加載請求,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器去完成。
  2. 每一層的類加載器都把類加載請求委派給父類加載器,直到所有的類加載請求都應該傳遞給頂層的啟動類加載器。
  3. 如果頂層的啟動類加載器無法完成加載請求,子類加載器嘗試去加載,如果連最初發(fā)起類加載請求的類加載器也無法完成加載請求時,將會拋出ClassNotFoundException,而不再調(diào)用其子類加載器去進行類加載。
    雙親委派 模式的類加載機制的優(yōu)點是java類它的類加載器一起具備了一種帶優(yōu)先級的層次關(guān)系,越是基礎(chǔ)的類,越是被上層的類加載器進行加載,保證了java程序的穩(wěn)定運行。

注意:

  • 并不是所有的類記載其都采用雙親委托機制
  • tomcat服務(wù)器類加載器也是用代理模式,所不同的是它首先嘗試去加載某個類,如果找不到再找代理給父類加載器。這與一般類加載器的順序是相反的。

我們可以簡單地自定義一個類加載器,用于加載某個class

public class FileSystemClassLoader extends ClassLoader {
    //文件的根目錄
    private String rootDir;

    public FileSystemClassLoader(String rootDir){
        this.rootDir=rootDir;
    }

    //重寫findClass方法
    @Override
    protected Class<?> findClass(String s) throws ClassNotFoundException {
        Class c=findLoadedClass(s);
        if (c!=null){
            return c;
        }else {
            ClassLoader parent=this.getParent();
            //parent獲取不到class時會拋出異常,為了繼續(xù)執(zhí)行使用try catch包裹
            try{
                c=parent.loadClass(s);
            }catch (Exception e){

            }
            if (c!=null){
                return  c;
            }else {
                byte[] classData=getClassData(s);
                if (classData==null){
                    throw new ClassNotFoundException();
                }else {
                    //將字節(jié)數(shù)組轉(zhuǎn)為Class
                    c=defineClass(s,classData,0,classData.length);
                }
            }
        }
        return c;
    }

    //將文件轉(zhuǎn)為字節(jié)數(shù)組
    private byte[] getClassData(String className) {
        //改為文件地址
        String path=rootDir+"/"+className.replace(".","/")+".class";
        System.out.println(path);
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        InputStream inputStream=null;
        try {
            inputStream=new FileInputStream(path);
            byte[] buffer=new byte[1024];
            int temp=0;
            while ((temp=inputStream.read(buffer))!=-1){
                byteArrayOutputStream.write(buffer,0,temp);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (byteArrayOutputStream!=null){
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

使用

public class UseCustomClassLoader {
    public static void main(String[]args){
        FileSystemClassLoader loader=new FileSystemClassLoader("/home/xjk");
        FileSystemClassLoader loader2=new FileSystemClassLoader("/home/xjk");
        try {
            Class clazz1=loader.findClass("com.jk.bean.Emp");//本項目自定義的類調(diào)用AppClassLoader
            System.out.println(clazz1.getClassLoader());
            Class clazz2=loader.findClass("java.lang.String");//rt.jar里的類調(diào)用BootstrapClassLoader
            System.out.println(clazz2.getClassLoader());
            Class clazz3=loader.findClass("com.company.Main");//項目外的類調(diào)用自定義的FileSystemClassLoader
            System.out.println(clazz3.getClassLoader());
            Class clazz4=loader2.findClass("com.company.Main");//使用不同類加載器,Class對象不一致
            System.out.println(clazz4.getClassLoader());
            System.out.println(clazz3==clazz4);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

sun.misc.Launcher$AppClassLoader@18b4aac2
null
com.jk.jvm.FileSystemClassLoader@1d44bcfa
com.jk.jvm.FileSystemClassLoader@6f94fa3e
false

因為BootstrapClassLoader無法被Java程序直接引用,所以顯示為空

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容