深入Java日記——類加載器

類加載器的作用

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

類緩存

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

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

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

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

  • 加載擴(kuò)展類和應(yīng)用程序類加載器,并指定他們的父類加載器。

  • 啟動(dòng)類加載器無法被Java程序直接引用

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

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

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

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

4.自定義類加載器

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

雙親委派模式

雙親委派模型

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

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

注意:

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

我們可以簡(jiǎn)單地自定義一個(gè)類加載器,用于加載某個(gè)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時(shí)會(huì)拋出異常,為了繼續(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");//本項(xiàng)目自定義的類調(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");//項(xiàng)目外的類調(diào)用自定義的FileSystemClassLoader
            System.out.println(clazz3.getClassLoader());
            Class clazz4=loader2.findClass("com.company.Main");//使用不同類加載器,Class對(duì)象不一致
            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

因?yàn)锽ootstrapClassLoader無法被Java程序直接引用,所以顯示為空

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

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

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