天天看點

Java 類加載機制

類的加載:

Java 類加載機制

類的初始化:

類什麼時候才被初始化:

1)建立類的執行個體,也就是new一個對象

2)通路某個類或接口的靜态變量,或者對該靜态變量指派

3)調用類的靜态方法

4)反射(Class.forName("com.lyj.load"))

5)初始化一個類的子類(會首先初始化子類的父類)

6)JVM啟動時标明的啟動類,即檔案名和類名相同的那個類

隻有這6中情況才會導緻類的類的初始化。

假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中,類隻能初始化一次),那就初始化直接的父類(不适用于接口)

加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。

類的加載指的是将類的.class檔案中的二進制資料讀入到記憶體中,将其放在運作時資料區的方法區内,然後在堆區建立一個這個類的Java.lang.Class對象,用來封裝類在方法區類的對象。

Java 類加載機制
類加載器:
Java 類加載機制

import java.net.URL;

public class Main {
    public static void main(String[] args) {
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }
    }
}      
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/resources.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/rt.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/sunrsasign.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jsse.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jce.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/charsets.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/lib/jfr.jar
file:/D:/Program%20Files/Java/jdk1.8.0_102/jre/classes      

Java類加載機制

類裝載器就是尋找類的位元組碼檔案,并構造出類在JVM内部表示的對象元件。在Java中,類裝載器把一個類裝入JVM中,要經過以下步驟:

(1) 裝載:查找和導入Class檔案;

(2) 連結:把類的二進制資料合并到JRE中;

    (a)校驗:檢查載入Class檔案資料的正确性;

    (b)準備:給類的靜态變量配置設定存儲空間;

    (c)解析:将符号引用轉成直接引用;

(3) 初始化:對類的靜态變量,靜态代碼塊執行初始化操作

類加載器和雙親委派模型

(1) Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,并且是虛拟機識别的(僅按照檔案名識别,如 rt.jar 名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛拟機記憶體中。啟動類加載器無法被Java程式直接引用

(2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目錄下的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫加載。開發者可以直接使用擴充類加載器。

(3) Application ClassLoader : 負責加載使用者類路徑(ClassPath)上所指定的類庫,開發者可直接使用。

雙親委派模型的工作過程:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此。是以所有的加載請求最終都應該傳達到頂層的啟動類加載器中,隻有當父加載器回報無法完成這個加載請求(它的搜尋範圍中沒有找到所需的類)時,子加載器才會嘗試自己去加載。

雙親委派模型要求除了頂層的啟動類加載器外,其餘的類加載器都應有自己的父類加載器。這些類加載器的父子關系不是以繼承的關系實作,而都是使用組合關系來複用父加載器的代碼。

public class ClassLoaderTest {
    public static void main(String[] args) {
        //輸出ClassLoaderText的類加載器名稱
        System.out.println("ClassLoaderText類的加載器的名稱:" + ClassLoaderTest.class.getClassLoader().getClass().getName());
        System.out.println("System類的加載器的名稱:" + System.class.getClassLoader());
        System.out.println("ArrayList類的加載器的名稱:" + ArrayList.class.getClassLoader());

        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        while (cl != null) {
            System.out.print(cl.getClass().getName() + "->");
            cl = cl.getParent();
        }
        System.out.println(cl);
    }
}      

Output:

ClassLoaderText類的加載器的名稱:sun.misc.Launcher$AppClassLoader
System類的加載器的名稱:null
ArrayList類的加載器的名稱:null
sun.misc.Launcher$AppClassLoader->sun.misc.Launcher$ExtClassLoader->null      

好處:java類随着它的類加載器一起具備了一種帶有優先級的層次關系。例如類java.lang.Object,它存放在rt.jar中,無論哪個類加載器要加載這個類,最終都會委派給啟動類加載器進行加載,是以Object類在程式的各種類加載器環境中都是同一個類。相反,如果使用者自己寫了一個名為java.lang.Object的類,并放在程式的Classpath中,那系統中将會出現多個不同的Object類,java類型體系中最基礎的行為也無法保證,應用程式也會變得一片混亂。