類加載機制

類加載的時機

主動引用(有且只有下面的五種)

  1. 使用new關(guān)鍵字創(chuàng)建對象時,訪問類的靜態(tài)字段時;

  2. 使用java.lang.reflect包的方法進行反射調(diào)用的時候;

  3. 初始化一個類時,如果父類沒有被初始化,則需要先初始化;

  4. 當(dāng)虛擬機啟動的時候,虛擬機先會初始化主類(main()方法所在的類)

  5. JDK1.7動態(tài)語言支持;

被動引用(類不會加載)

  1. 子類引用父類的靜態(tài)字段,不會導(dǎo)致子類的的初始化。

  2. 通過數(shù)組定義引用類。

  3. 訪問類中的常量;

類加載流程

  1. 加載

    1.1 加載二進制字節(jié)流到內(nèi)存

    1.2 生成方法區(qū)數(shù)據(jù)結(jié)構(gòu)

    1.3 生成java.lang.Class對象

  2. 連接

    2.1 驗證

    • 文件格式驗證: 版本號,是不是
    • 元數(shù)據(jù)驗證:是否基礎(chǔ)了final類?是不是有父類?是不是實現(xiàn)了抽象類的方法?
    • 字節(jié)碼驗證:跳轉(zhuǎn)指令是否合法?
    • 字符引用驗證:引用的類是否存在?類及接口是否有訪問權(quán)限?

    2.2 準(zhǔn)備
    類變量(即靜態(tài)變量而不是實例變量)分配內(nèi)存,并為類設(shè)置初始值(方法區(qū))

    • public static int v=1;
    • 在準(zhǔn)備階段中個,v會被設(shè)置為0
    • 在初始化的<clinit>中才會被設(shè)置為1
    • 對于static final類型,在準(zhǔn)備階段就會被賦上準(zhǔn)確的值
    • public static final int v=1

    2.3 解析

    • 字符引用轉(zhuǎn)換為直接引用:com.XXX.sudent這個就是個符號,直接引用就是它在內(nèi)存的地址
  3. 初始化
    執(zhí)行類構(gòu)造器<clinit>方法,<clinit>方法由靜態(tài)代碼塊、靜態(tài)變量構(gòu)成和賦值語句構(gòu)成。

    • <clinit>方法線程安全: 基于它可以實現(xiàn)線程安全單例,參考單例模式
    • <clinit>方法執(zhí)行時,發(fā)現(xiàn)父類的沒有執(zhí)行先執(zhí)行父類的<clinit>方法
  4. 使用

  5. 卸載

類加載器

類加載器分類

  1. 啟動類加載器

  2. 擴展加載器

  3. 應(yīng)用類加載器

  4. 自定義類加載器

備注:類加載器之間不是繼承的關(guān)系,而是組合的關(guān)系。

雙親委派模式

類加載器:字底向上查找是否已經(jīng)加載類,自頂向下加載類。

雙親委派模式:為了維護類加載的安全。

破壞雙親委派模式

  1. 接口在rt.jar實現(xiàn)在應(yīng)用程序,啟動類加載器如何加載應(yīng)用類加載器的類,例如:SPI機制

    線程上線文類加載器可以解決這個問題,它模式的類加載器是應(yīng)用類加載器。

  2. 熱替換

問題 1:

static{
    a = 300;
    System.out.println(a);//這里會報錯,聲明語句之前只能賦值不能引用
}
public static int a = 10;

問題 2:

類加載中只用類變量及靜態(tài)代碼執(zhí)行,那么構(gòu)造方法、普通代碼塊啥時候執(zhí)行呢,其實這個不屬于類加載,它屬于類的實例化,在類的實例化是執(zhí)行。

public class Line {  
    static {  
        System.out.println("靜態(tài)代碼塊執(zhí)行:loading line");  
    }  
  
    public static String s = getString();  
  
    private static String getString() {  
        System.out.println("給靜態(tài)變量賦值的靜態(tài)方法執(zhí)行:loading line");  
        return "ss";  
    }  
  
    public static void test() {  
        System.out.println("普通靜態(tài)方法執(zhí)行:loading line");  
    }  
  
    public Line() {  
        System.out.println("構(gòu)造方法執(zhí)行:loading line");  
    }  
  
    {  
        System.out.println("構(gòu)造代碼塊執(zhí)行");  
    }  
}
public class CodeBlockTest {  
    public static void main(String[] args) {  
        System.out.println("主方法");  
        {  
            System.out.println("main方法中最開始的,普通代碼塊執(zhí)行");  
        }  
        Line line = new Line();  
        System.out.println("...............");  
        Line line1 = new Line();  
        System.out.println("...............");  
        {  
            System.out.println("main方法中結(jié)尾事的,普通代碼塊執(zhí)行");  
        }  
    }  
}

結(jié)果大家自己執(zhí)行,注意下:構(gòu)造代碼塊在創(chuàng)建對象時被調(diào)用,每次創(chuàng)建對象都會被調(diào)用,并且構(gòu)造代碼塊的執(zhí)行次序優(yōu)先于類構(gòu)造函數(shù)。

問題 3:

public class Singleton {
    private static Singleton mInstance = new Singleton();// 位置1
    public static int counter1;
    public static int counter2 = 0;
//        private static Singleton mInstance = new Singleton();// 位置2
    private Singleton() {
        counter1++;
        counter2++;
    }

    public static Singleton getInstance() {
        return mInstance;
    }
}

class InitDemo {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println("counter1: " + singleton.counter1);
        System.out.println("counter2: " + singleton.counter2);
    }
}
位置1:
counter1=1
counter2=0

位置2:
counter1=1
counter2=1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容