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