Java類加載機制(一)

類加載機制

JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成
JVM能夠直接運行的Java類型的過程。

類加載過程

Java類加載的過程:
    加載————>驗證->準備->解析————>初始化————>使用————>卸載
    (驗證->準備->解析  統稱爲連接)
  • 加載java

    將class文件字節碼內容加載到內存中,並將這些靜態數據轉換爲方法區的運行 時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲 方法區類數據訪問的入口。(這個過程須要類加載器參與)數組

  • 連接緩存

    驗證: —— 確保加載的類信息符合JVM規範,沒有安全方面的問題。 準備: —— 正式爲類變量(static變量)分配內存並設置類變量初始值的接段, 這些內存都將在方法區中進行分配。 解析: —— 虛擬機產量池內的符號引用替換爲直接引用的過程。對同一個符號引用進行屢次解析請求是很常見的,虛擬機實現可能會對第一次解析的結果進行緩存(將直接引用保存在運行時常量池中),解析動做主要針對類或接口、字段、類方法、接口方法四類符號引用進行,。安全

符號引用:用一組任何形式的字面量描述所引用的目標,符號引用與虛擬機實現的內存佈局無關,若果有了符號引用,引用目標並不必定加載到內存中。 數據結構

直接引用:直接指向目標的指針、相對偏移量或一個能間接定位到目標的句柄,符號引用與虛擬機實現的內存佈局無關,若是有了直接引用,引用目標必定加載到內存中。多線程

  • 初始化佈局

    (1)初始化階段執行類構造器()方法的過程,<clinit>()方法是由 編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊(static塊)中的語句 合併產生的。spa

    (2)當一個類被初始化的時候,若是其父類沒有被初始化時,則先初始化其父類。線程

    (3)虛擬機保證一個類的<clinit>()方法在多線程環境中被準確加鎖和同步,即線程安全。指針

    (4)當訪問一個Java類的靜態域時,只有真正聲明這個域的類纔會被初始化。

/**
 * static代碼塊按順序執行,
 * 初始話子類時,父類沒有初始化,則先初始化父類
 */
public class Demo {
    static {
        System.out.println("Demo靜態初始化塊");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println("Demo的main方法");
        A a = new A();
    }
}

class A extends A_Father {
    static {
        System.out.println("A靜態初始化塊");
    }

    public A() {
        System.out.println("建立A對象");
    }
}

class A_Father {
    static {
        System.out.println("A_Father靜態初始化塊");
    }

}

//執行結果
Demo靜態初始化塊
Demo的main方法
A_Father靜態初始化塊
A靜態初始化塊
建立A對象

主動引用

  • 主動引用

    (1)new一個類對象

    (2)調用類的靜態成員(除去final常量)和靜態方法

    (3)使用java.lang.reflect包的方法對類進行反射調用

    (4)當虛擬機啓動,java Demo,則必定初始化Demo類,其實就是先啓動main方法所在的類

    (5)初始化一個類,若是它的父類沒有被初始化,則先初始化它的父類

public class Demo {
    //調用類的靜態方法
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println("Demo的main方法");
        //調用類的靜態成員
        A a = new A();
        //反射
        Class.forName("leijiazai.a.A");//沒有初始化信息,即一個類只被初始化一次。
    }
}

class A extends A_Father {
    private static final int i = 100;

    static {
        System.out.println("A靜態初始化塊");
    }

    public A() {
        System.out.println("建立A對象");
    }
}
//A的父類
class A_Father {
    static {
        System.out.println("A_Father靜態初始化塊");
    }

}
//結果
Demo的main方法
A_Father靜態初始化塊
A靜態初始化塊
建立A對象

被動引用

  • 被動引用

    (1)訪問一個靜態域,只有真正聲明這個域的類纔會被初始化 ———— 經過子類引用父類的靜態變量,不會致使子類被初始化

    (2)經過數組定義類引用,不會觸發此類的初始化

    (3)引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中)

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        //訪問一個A父類靜態域
        System.out.println(A.j);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~");
        //數組定義類引用
        A[] as = new A[10];//沒有輸出
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~");
        //引用常量
        System.out.println(A.i);
    }
}

class A extends A_Father {
    public static int i = 100;

    static {
        System.out.println("A靜態初始化塊");
    }

    public A() {
        System.out.println("建立A對象");
    }
}

class A_Father {
    public static int j = 200;

    static {
        System.out.println("A_Father靜態初始化塊");
    }

}
//結果
A_Father靜態初始化塊
200
~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~
A靜態初始化塊
100

http://chenzehe.iteye.com/blog/1727062

相關文章
相關標籤/搜索