什麼是類的加載:jvm將class文讀取到內存中,通過對class文件的校驗、轉換解析、初始化最終在jvm的heap和方法區分配內存造成能夠被jvm直接使用的類型的過程。java
生命週期:7個階段依次爲:Loading(加載)Verification (驗證)Preparation (準備)Resolution(鏈接) Initialization (初始化)Using(使用) Unloading(卸載)c++
這個階段jvm完成如下動做:
首先 類加載器經過類的全路徑限定名讀取類的二進制字節流,
而後 將二進制字節流表明的類結構轉化到運行時數據區的 方法區中,
最後 在jvm堆中生成表明這個類的java.lang.Class實例(不是這個類的實例)程序員
獲取類的二進制流 既可使用jvm自帶的類加載器,也能夠本身寫加載器來加載,這一小步是徹底可控的。不一樣的加載器能夠從各類地方讀取:zip包jar包,class文件,網絡流 。。。讀取類的二進制字節流bootstrap
同一個加載器加載的同源類纔是真的同類。不一樣加載器加載同源類,不是同類!instanceof爲FALSE安全
各個加載器都是先委託本身的父加載器加載類,若確實沒加載到再本身來加載網絡
因而java默認的類查找加載順序是自頂向下的,樹狀結構jvm
雙親委託的意圖是保證java類型體系中最基礎的行爲一致,優先加載JDK中的類模塊化
加載器主要有四種:函數
jvm啓動類加載器bootstrap loader,用c++實現爲jvm的一部分(僅指sun的hotspot),負責 JAVA_HOME/lib下面的類庫中的類的加載,這個加載器,java程序沒法引用到。url
擴展類加載器Extension Loader,由sun.misc.Launcher$ExtClassLoader類實現,可在java中使用,負責JAVA_HOME/lib/ext 目錄和java.ext.dir目錄中類庫的類的加載。
應用系統類加載器Application System Loader,由sun.misc.Louncher$AppClassLoader實現,負責加載用戶類路徑中類庫中的類,若是沒有使用自定義的加載器,這個就是默認的 加載器!
用戶自定義加載器 本身定義從哪裏加載類的二進制流
1,類加載的過程 Java程序運行的場所是內存,當在命令行下執行: java HelloWorld 命令的時候,JVM會將HelloWorld.class加載到內存中,並造成一個Class的對象HelloWorld.class。 其中的過程就是類加載過程: 一、尋找jre目錄,尋找jvm.dll,並初始化JVM; 二、產生一個Bootstrap Loader(啓動類加載器); 三、Bootstrap Loader自動加載Extended Loader(標準擴展類加載器),並將其父Loader設爲Bootstrap Loader。 四、Bootstrap Loader自動加載AppClass Loader(系統類加載器),並將其父Loader設爲Extended Loader。 五、最後由AppClass Loader加載HelloWorld類。 2,類加載器各自搜索的目錄 Bootstrap、 ExtClassLoader、 AppClassLoader都是類加載器,Bootstrap是本地代碼編寫的,而ExtClassLoader、 AppClassLoader都是都java編寫的,都在rt.jar中。 一、Bootstrap Loader(啓動類加載器):加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。 二、Extended Loader(標準擴展類加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運行程序時,也能夠指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld 三、AppClass Loader(系統類加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運行程序時,也能夠加上-cp來覆蓋原有的Classpath設置,例如: java -cp ./lavasoft/classes HelloWorld ExtClassLoader和AppClassLoader在JVM啓動後,會在JVM中保存一份,而且在程序運行中沒法改變其搜索路徑。若是想在運行時從其餘搜索路徑加載類,就要產生新的類加載器。 3,類加載器的特色 一、運行一個程序時,老是由AppClass Loader(系統類加載器)開始加載指定的類。 二、在加載類時,每一個類加載器會將加載任務上交給其父,若是其父找不到,再由本身去加載。 三、Bootstrap Loader(啓動類加載器)是最頂級的類加載器了,其父加載器爲null. 4,類加載器的獲取 public class HelloWorld { public static void main(String[] args) { HelloWorld hello = new HelloWorld(); Class c = hello.getClass(); ClassLoader loader = c.getClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent()); } } 運行結果: sun.misc.Launcher$AppClassLoader@5c647e05 sun.misc.Launcher$ExtClassLoader@3d4eac69 null 從上面的結果能夠看出,並無獲取到ExtClassLoader的父Loader,緣由是Bootstrap Loader(啓動類加載器)是用C語言實現的,找不到一個肯定的返回父Loader的方式,因而就返回null。 5,類的加載 類加載有三種方式: 一、命令行啓動應用時候由JVM初始化加載 二、經過Class.forName()方法動態加載 三、經過ClassLoader.loadClass()方法動態加載 三種方式區別比較大,看個例子就明白了: public class HelloWorld { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = HelloWorld.class.getClassLoader(); System.out.println(loader); //使用ClassLoader.loadClass()來加載類,不會執行初始化塊 loader.loadClass("Test2"); //使用Class.forName()來加載類,默認會執行初始化塊 // Class.forName("Test2"); //使用Class.forName()來加載類,並指定ClassLoader,初始化時不執行靜態塊 // Class.forName("Test2", false, loader); } } public class Test2 { static { System.out.println("靜態初始化塊執行了!"); } } 分別切換加載方式,會有不一樣的輸出結果。 6,自定義ClassLoad 爲了說明問題,先看例子: package test; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class MyClassLoader { public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { URL url = new URL("file:/E:\\projects\\testScanner\\out\\production\\testScanner"); ClassLoader myloader = new URLClassLoader(new URL[]{url}); Class c = myloader.loadClass("test.Test3"); System.out.println("----------"); Test3 t3 = (Test3) c.newInstance(); } } public class Test3 { static { System.out.println("Test3的靜態初始化塊執行了!"); } } 運行後: ---------- Test3的靜態初始化塊執行了! Process finished with exit code 0 能夠看出自定義了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3加載到內存了,並經過默認構造方法構造了對象Test3 t3 = (Test3) c.newInstance(); 有關ClassLoader還有很重要一點: 同一個ClassLoader加載的類文件,只有一個Class實例。可是,若是同一個類文件被不一樣的ClassLoader載入,則會有兩份不一樣的ClassLoader實例(前提是着兩個類加載器不能用相同的父類加載器)。
雙親委派是java設計者推薦的類加載器實現方式,能夠在遵循的基礎上擴展,自定義類加載器的實現機制。
OSGi事實上的java模塊化標準,他自定義的類加載器,能不少好實現模塊化和模塊的熱部署:更換一個bundle時,連同這個bundle的類加載器一同換掉。
OSGi中java.*開頭的類按照雙親加載機制加載,而其餘類則都是由平級的類加載器加載的,造成一張網。
Loading和 驗證是交叉進行的,驗證二進制字節流表明的字節碼文件是否合格,主要從一下幾方面判斷:
文件格式:參看class文件格式詳解,通過文件格式驗證以後的字節流才能進入方法區分配內存來存儲。
元數據驗證:是否符合java語言規範
字節碼驗證:數據流和控制流的分析,這一步最複雜
符號引用驗證:符號引用轉化爲直接引用時(解析階段),檢測對類自身之外的信息進行存在性、可訪問性驗證
若是確認代碼安全無誤,可用 -Xverify:none關閉大部分類的驗證,加快類加載時間
在方法區中給類的類變量(static修飾)分配內存
而後初始化其值,若是類變量是常量,則直接賦值爲該常量值不然爲java類型的默認的零值。
指將常量池內的符號引用替換爲直接引用的過程。
這個階段才真正開始執行java代碼,靜態代碼塊和設置變量的初始值爲程序員設定的值
主動引用
有且只有下面5種狀況纔會當即初始化類,稱爲主動引用:
new 對象時
讀取或設置類的靜態字段(除了 被final,已在編譯期把結果放入常量池的 靜態字段)或調用類的靜態方法時;
用java.lang.reflect包的方法對類進行反射調用沒初始化過的類時
初始化一個類時發現其父類沒初始化,則要先初始化其父類
含main方法的那個類,jvm啓動時,須要指定一個執行主類,jvm先初始化這個類
1.首先初始化父類的static變量和塊,按出現順序
2.初始化子類的static變量和塊,按出現順序
3.初始化父類的普通變量,調用父類的構造函數
4.初始化子類的普通變量,調用子類的構造函數
類的初始化過程發生時刻:
1. T是一個類,當T的一個實例建立的時候,也就是T t = new T();
2. T的一個靜態方法被調用的時候,也就是 T.staticField();
3. T的靜態屬性被賦值的時候,T.staticField = o;
4. T的一個靜態屬性被使用的時候,也就是 Object o = T.staticField; 可是它不是常量。
5. T is a top level class , and an assert statement lexically nested
Bootstrap、 ExtClassLoader、 AppClassLoader都是類加載器,Bootstrap是本地代碼編寫的,而ExtClassLoader、 AppClassLoader都是都java編寫的,都在rt.jar中。
public class HelloWorld {
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
Class c = hello.getClass();
ClassLoader loader = c.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
運行結果:
sun.misc.Launcher$AppClassLoader@5c647e05
sun.misc.Launcher$ExtClassLoader@3d4eac69
null
從上面的結果能夠看出,並無獲取到ExtClassLoader的父Loader,緣由是Bootstrap Loader(啓動類加載器)是用C語言實現的,找不到一個肯定的返回父Loader的方式,因而就返回null。