JAVA类加载机制
java類加載機(jī)制
類加載過程
-
加載(Loading)
Loading is the process of finding the binary representation of a class or interface type with a particular name and creating a class or interface from that binary representation.
? 通過類全名獲取其class的二進(jìn)制流。 在java堆中生成一個代表該類的Class對象
-
驗證(Verification)
Verification ensures that the binary representation of a class or interface is structurally correct . Verification may cause additional classes and interfaces to be loaded but need not cause them to be verified or prepared.
? 驗證class文件格式正確性
-
準(zhǔn)備(Preparation)
Preparation involves creating the static fields for a class or interface nd initializing such fields to their default values . This does not require the execution of any Java Virtual Machine code; explicit initializers for static fields are executed as part of initialization, not preparation.
? 準(zhǔn)備階段主要是靜態(tài)變量創(chuàng)建,賦予默認(rèn)值(隱式初始化)
-
解析(Resolution)
Resolution is the process of dynamically determining concrete values from symbolic references in the run-time constant pool.
將符號應(yīng)用確定為具體實際應(yīng)用
-
初始化(Initialization)
Initialization of a class or interface consists of executing its class or interface
initialization method
? 執(zhí)行類初始化方法
類加載器(ClassLoader)
類加載器分類:
-
啟動類加載器(Bootstrap ClassLoader)
加載jre lib下包,c語言實現(xiàn),未有對應(yīng)java實現(xiàn)
//例如打印String類的classloader就是啟動類加載器,會輸出null System.out.println(String.class.getClassLoader()); -
擴(kuò)展類加載器 (Extend ClassLoader)
加載ext目錄下包 -
應(yīng)用程序類加載器(Application ClassLoader)
加載應(yīng)用程序classpath下包,默認(rèn)應(yīng)用程序使用其加載
public class Test {public static void main(String[] args) {/**創(chuàng)建一個Test類,打印器classloader,輸出結(jié)果為:sun.misc.Launcher$AppClassLoader@18b4aac2*/System.out.println(Test.class.getClassLoader());} } -
自定義類加載器(UserDefined ClassLoader):
自定義加載類,這個就需要自己繼承 ClassLoader類來實現(xiàn)自定義加載過程。
父類委派機(jī)制
當(dāng)加載一個類時當(dāng)前類加載器會首先委托其父類加載器進(jìn)行加載該類,當(dāng)父類無法加載該類其在委托其父類加載器進(jìn)行加載該類,直到啟動類加載器也不能加載該類。則在一層層回傳其子類加載器進(jìn)行加載該類。
例如要加載java.lang.String類,appClassloader委托extClassLoader進(jìn)行加載,extClassLoader發(fā)現(xiàn)自己加載不了,在委托BootClassLoader,BootClassLoader發(fā)現(xiàn)rt.jar包有該類,好吧我加載,ext和appLoader是永遠(yuǎn)無法自己加載String類的。
再例如,現(xiàn)在要加載用戶自己開發(fā)的一個引用類,com.x.Test。雖然apploader發(fā)現(xiàn)自己可以找到該類的class文件,但是也不能直接加載,還是要委托ext,再到boot。最后boo和ext發(fā)現(xiàn)機(jī)制都加載不了,最后apploader才能自己加載。
自定義一個java.lang.String
package java.lang; public class String {public static void main(String[] args) {System.out.println("myself string class");} } /** 結(jié)果輸出信息: 錯誤: 在類 java.lang.String 中找不到 main 方法, 請將 main 方法定義為:public static void main(String[] args) 否則 JavaFX 應(yīng)用程序類必須擴(kuò)展javafx.application.Application */父類委派機(jī)制的好處:
1、安全。java平臺核心庫API永遠(yuǎn)只會被bootLoader加載,防止篡改
2、避免重復(fù)加載,當(dāng)父類加載器已經(jīng)加載過該類字類加載器沒必要再加載一遍。
雙親委派加載實現(xiàn)代碼,看下ClassLoader的loadclass方法源碼,去掉一些不影響邏輯代碼
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded//先檢查該類是否已被加載Class<?> c = findLoadedClass(name);if (c == null) {//未被加載long t0 = System.nanoTime();try {//若果parent不為空,再委派自己的父類加載器進(jìn)行加載if (parent != null) {c = parent.loadClass(name, false);} else {//若果parent為空,說明好了,到頭了,應(yīng)該委托bootLoader加載了c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}//上面父類一系列加載,還是空,那么我自己加載if (c == null) {// If still not found, then invoke findClass in order// to find the class.c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}}》》打破父類委派機(jī)制
為什么要打破父類委派機(jī)制?
tomcat容器是個最好的實例,class要熱部署,還有可以跑多個應(yīng)用,允許一個類被多次加載,如兩個應(yīng)用加載同一個jar。應(yīng)用之間要做到相互屏蔽不影響,就應(yīng)該加載多次。
tomcat自定義類加載器進(jìn)行類加載。像這種很多Spring也是有自己的類加載器。
自定義類加載器
繼承ClassLoader類,重新findClass方法
public class MyClassLoader extends ClassLoader {private String basePath;public MyClassLoader(String basePath){this.basePath = basePath;}private byte[] loadClassBytes(String name) throws IOException {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(this.basePath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}/*** 要重寫該方法,加載獲取class文件二進(jìn)制數(shù)據(jù)* @param name*/@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] data = new byte[0];try {data = loadClassBytes(name);} catch (IOException e) {e.printStackTrace();}return defineClass(name , data, 0, data.length);} }sun.misc.Launcher$AppClassLoader
編寫一個測試類
package com.bean; public class Test {static {ClassLoader currLoader = Test1.class.getClassLoader();System.out.println(currLoader.getClass().getName());System.out.println(currLoader.getParent().getClass().getName());} }使用自定義類加載器進(jìn)行上面類加載
public static void main(String[] args) throws Exception {MyClassLoader loader = new MyClassLoader("D:/loader");//Test.class文件要按包路徑名放到D:/loader下Class c = loader.loadClass("com.bean.Test");c.newInstance();}/**Test類加載成功newInstance實例化時候執(zhí)行靜態(tài)塊方法輸出結(jié)果:com.test.jvm.MyClassLoadersun.misc.Launcher$AppClassLoader??為什么父類加載器是AppClassLoader因為自定義類加載器也需要被加載,默認(rèn)開發(fā)的java都是被AppClassLoader加載的。也就是MyClassLoader是被AppLoader加載的,MyClassLoader又加載了Test,所有Test的父類加載器是AppLoader!!!注意如果是在開發(fā)工具中,Test類要在工程中刪除,否則工程中即classpath下有Test.class文件,會由于雙親委派機(jī)制被AppClassLoader加載到,不會被自定義類加載器加載。*/總結(jié)
- 上一篇: html用变量存储颜色信息,我如何使用间
- 下一篇: SWAT模型在水文水资源、面源污染模拟中