深刻分析ClassLoader

首先介紹下ClassLoader:
ClassLoader顧名思義就是類加載器,負責將Class加載到JVM中,事實上ClassLoader除了能將Class加載到JVM中以外,還有一個重要的做用就是審查每一個類應該有誰加載,ClassLoader是一個父優先的等級加載機制。ClassLoader除了上述兩個做用外還有一個任務就是將Class字節碼從新解析成JVM統一要求的對象格式-----------由此本文能夠劃分紅三點java

  1. ClassLoader類結構分析
    1. ClassLoader經常使用的方法
    輸入圖片說明
    1,defineClass():用來將byte字節流解析成JVM可以識別的Class對象
    2,findClass():用來子類擴展的方法,目的是爲了尋找類
    3,loadClass():加載類,這裏就能夠動態加載了,程序運行的時候加載
    4,resolveClass():這個方法是用來連接這個類
    JDK給咱們提供了能夠擴展的,也就是咱們能夠自定義的類加載器URLCLassLoader,比較方便app

    2. ClassLoader的等級加載機制

    輸入圖片說明

    1,BootStrap ClassLoader:
    這是一個引導類加載器,首先聲明一下雖然說在類的結構中能夠看到這個類,可是這個類只有Class文件,據瞭解這個類是經過C++編寫的,這也就是ExtClassLoader的父類爲空的緣由。引導類負責加載JDK的jre下的類庫,例如rt.jarjsp

    2,ExtClassLoader:
    這是一個擴展類加載器,加載bin目錄下的ext文件夾下的jar包,很少說ide

    3,AppClassLoader:
    這個類加載器就是咱們自定義的類,例如咱們的這個方法getSystemClassLoader()做爲父加載器,這個加載器也就是AppClassLoader。測試

    4,加載類過程總結:
    看了不少資料都是什麼雙親委託加載機制,具體我也不是很清楚,能夠我太low,可是我我的的理解就是,當加載一個類的時候:首先檢查這個類是否已經被本身加載過,若是已經加載過,就拒絕本次加載,若是沒有加載過,就會拋給他的父類,而後父類在檢查是否加載,而後在拋給父類,直到所有檢查完都沒有發現這個類被加載,那麼就有意思了,由於每個類加載器都有本身的加載範圍,而後開始判斷,父類不加載類,那麼就開始向下尋找這個類的加載器,而後這個類被加載,也就是先向上檢查是否加載,其次向下看是否能夠加載!--------------------這裏有一個講解,來幫助理解,若是咱們本身寫了一個java.lang.String,那麼類加載器就先去加載jdk下的java.lang.String.遇到咱們本身定義的類就不會加載了,這也就是java.lang.Object首先被加載,父類加載會防止咱們破壞干擾jre的正常運行,致使類結構被破壞this

這裏是類關係的代碼spa

public class ClassA {
}
public class ClassB {
}
public class ClassTest {
    public static void main(String[] args) {
        //演示 我們的類加載器
        System.out.println("ClassTest-------------ClassLoader:"+ClassTest.class.getClassLoader());
        System.out.println("ClassTest.Parent------ClassLoader:"+ClassTest.class.getClassLoader().getParent());
        System.out.println("ClassTest.GrandFather-ClassLoader:"+ClassTest.class.getClassLoader().getParent().getParent());
        
        //用戶自定義的類加載   都是經過AppClassLoader加載
        System.out.println(ClassA.class.getClassLoader());
        System.out.println(ClassB.class.getClassLoader());
        //結論就是----------當兩個類名稱不一樣,那麼這兩個類就不相同
    }
}

打印結果3d

ClassTest-------------ClassLoader:sun.misc.Launcher$AppClassLoader@73d16e93
ClassTest.Parent------ClassLoader:sun.misc.Launcher$ExtClassLoader@15db9742
ClassTest.GrandFather-ClassLoader:null
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$AppClassLoader@73d16e93
  1. 如何判斷兩個類相同
  • 類名稱不一樣
這裏就不須要介紹了,只要類名不一樣,那麼類確定也不一樣
  • 包名稱不一樣
package me.classloader.classpackage;

public class String {

}


package me.classloader.classpackage;

import org.junit.Test;

public class ClassTest {
    @Test
    public void classTest(){
        //這裏是  BootStrap ClassLoader-------所以返回Null
        System.out.println(java.lang.String.class+"------classLoader:"+java.lang.String.class.getClassLoader());
        //這裏 咱們自定義的類        被AppClassLoader 加載
        System.out.println(String.class+"--------ClassLoader:"+String.class.getClassLoader());
        //很明顯這兩個類是不一樣的
    }
}
這裏能夠獲得咱們的包名能夠引發 兩個類不是同一個類,而且,這裏JVM就近調用一個類先調用的同包下的類
  • 類加載器不一樣
package me.classloader.classloader;

public class Demo {
    private Demo demo;

    public void setDemo(Demo demo) {
        this.demo = demo;
    }
    
}

package me.classloader.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

//本身寫的ClassLoader
public class MyClassLoader extends ClassLoader {

    private String classPath;// 類路徑

    public MyClassLoader() {
    }

    public MyClassLoader(String classPath) {

        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        byte[] classDate = getDate(name);
        if (classDate == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classDate, 0, classDate.length);
        }
    }

    // 將 class 文件轉化成字節流
        //className是類名稱---權限定名
    private byte[] getDate(String className) {
        // 將XX.XX.java --------->XX/XX/java.class
        String path = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int num = 0;
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0, num);
            }
            return stream.toByteArray();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

package me.classloader.classloader;

public class ClassTest {
    public static void main(String[] args) throws Exception{
        String classPath ="E:\\javaprogram\\java-exercise\\classload\\bin";
        String className = "me.classloader.classloader.Demo";
        ClassLoader appClassLoader = ClassTest.class.getClassLoader();
        MyClassLoader myClassLoader = new MyClassLoader(classPath);
        Class demo1 = appClassLoader.loadClass(className);
        System.out.println(demo1.newInstance().getClass().getClassLoader());
        Class demo2 = myClassLoader.findClass(className);
        System.out.println(demo2.newInstance().getClass().getClassLoader());
        System.out.println(demo1.newInstance().getClass() ==demo2.newInstance().getClass());
    }
}
測試結果
sun.misc.Launcher$AppClassLoader@73d16e93
me.classloader.classloader.MyClassLoader@6d06d69c
false

經過上面的代碼,咱們也知道了如何寫一個類加載器,其實查看findClass()是ClassLoader提供的一個模板方法!代理

總結:類加載器也是一個比較深的東西,例如servlet中jsp使用了熱部署!其實就是類加載器的應用,可能動態代理也用到了類加載器,由於實際工做加載的是咱們代理的類code

相關文章
相關標籤/搜索