類加載機(jī)制與反射(一)

類的加載、連接和初始化

1.JVM和類

當(dāng)調(diào)用java命令運(yùn)行某個(gè)java程序時(shí),該命令會(huì)啟動(dòng)一個(gè)java虛擬機(jī)進(jìn)程,不管該Java程序有多么復(fù)雜,該程序啟動(dòng)了多少個(gè)線程,它們都處于該java虛擬機(jī)進(jìn)程里。
當(dāng)系統(tǒng)出現(xiàn)以下幾種情況時(shí),JVM進(jìn)程將被終止:
(1)程序運(yùn)行到最后正常結(jié)束
(2)使用 Systerm.exit()或者 Runtime.getRuntime().exit()
(3)執(zhí)行過程中遇到未捕獲的異?;蛘咤e(cuò)誤而結(jié)束
(4)程序所在平臺(tái)強(qiáng)制結(jié)束JVM進(jìn)程

例子:
A.java

package com.example;

public class A
{
    //類變量
    public static int a = 5;
    public static void main(String[] args)
    {
    }
}

AText.java


package com.example;

/**
 * Created by zhoujian on 2017/4/3.
 */

public class AText
{
    public static void main(String[] args)
    {
        A a = new A();
        a.a++;
        //運(yùn)行結(jié)果:6
        System.out.println(a.a);
    }
}

BText.java

package com.example;

/**
 * Created by zhoujian on 2017/4/3.
 */

public class BText
{
    public static void main(String[] args)
    {
        A b = new A();
        //運(yùn)行結(jié)果:5
        System.out.println(b.a);
    }
}

兩次運(yùn)行,處于不同的JVM中,兩個(gè)JVM之間并不會(huì)共享數(shù)據(jù)

2.類的加載

當(dāng)程序使用某個(gè)類時(shí),如果該類該類還未被加載到內(nèi)存中,則系統(tǒng)會(huì)通過加載、連接和初始化這三個(gè)步驟來對(duì)該類進(jìn)行初始化。這三個(gè)步驟稱為類加載或類初始化

類加載指的是將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.Class對(duì)象

類的加載由類加載器完成,類加載器通常由JVM提供

通過不同的類加載器,可以從不同來源加載類的二進(jìn)制數(shù)據(jù)

  • 從本地系統(tǒng)加載class 文件
  • 從JAR包加載class文件
  • 通過網(wǎng)絡(luò)加載class文件
  • 把一個(gè)java 文件動(dòng)態(tài)編譯,并執(zhí)行加載

類加載器無需等到使用時(shí)才去加載該類,java 虛擬機(jī)規(guī)范允許系統(tǒng)預(yù)先加載某些類

3.類的連接

當(dāng)類被加載之后,系統(tǒng)為之生成一個(gè)對(duì)應(yīng)的Class 對(duì)象,接著將進(jìn)入連接階段,連接階段負(fù)責(zé)把二進(jìn)制數(shù)據(jù)合并到JRE中。類連接又分為三個(gè)階段

  • 驗(yàn)證:用于檢驗(yàn)被加載的類是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致
  • 準(zhǔn)備:類準(zhǔn)備階段負(fù)責(zé)為類的類變量分配內(nèi)存,并設(shè)置默認(rèn)初始值
  • 解析:將類的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用

4.類的初始化

在類的初始化階段,虛擬機(jī)負(fù)責(zé)對(duì)類進(jìn)行初始化,主要是對(duì)類變量初始化。
在java類中對(duì)變量指定初始值有兩種方式:

  • 聲明類變量時(shí)指定初始值
  • 使用靜態(tài)初始化塊為類變量指定初始值

package com.example;

/**
 * Created by zhoujian on 2017/4/3.
 */

public class AText
{
    //聲明變量 b時(shí)指定初始值
    static int b = 2;
    static
    {
        //使用靜態(tài)初始化塊為變量b指定初始值
        b = 5;

    }


    public static void main(String[] args)
    {

        A a = new A();
        a.a++;
        System.out.println(a.a);
        System.out.println(AText.b);
    }
}

5.類初始化的時(shí)機(jī)

當(dāng) Java 程序首次通過下面6種方式來使用某個(gè)類或者接口,系統(tǒng)就會(huì)初始化該類或者接口

  • 創(chuàng)建類的實(shí)例
  • 調(diào)用某個(gè)類的方法(靜態(tài)方法)
  • 訪問某個(gè)類或接口的類變量,或者為該類變量賦值
  • 使用反射的方式來強(qiáng)制創(chuàng)建某個(gè)類或者接口對(duì)應(yīng)的 java.lang.Class 對(duì)象
  • 初始化某個(gè)類的子類。那么該子類所有父類都會(huì)被初始化
  • 直接使用java.exe 命令來運(yùn)行某個(gè)主類

對(duì)于一個(gè)final 型的類變量,如果該類變量在編譯的時(shí)就可以確定下來,那么這個(gè)類變量相當(dāng)于“宏變量”。java 編譯器會(huì)在編譯時(shí)直接把這個(gè)類變量出現(xiàn)的地方替換成它的值,因此即使程序使用該靜態(tài)類變量,也不會(huì)導(dǎo)致該類的初始化

package com.example;

public class MyTest
{

    static
    {
        //靜態(tài)初始化塊

    }

    //變量name可以在編譯的時(shí)候確定下來,相當(dāng)于一個(gè)常量
    static final String name = "zhoujian";
    
    public static void main(String[] args)
    {
        //不會(huì)初始化MyTest類
        System.out.println(MyTest.name);
    }

}

當(dāng)使用ClassLoader 類的loadClass()方法來加載某個(gè)類時(shí),該方法只是加載類,并不會(huì)執(zhí)行該類的初始化。

使用Class的forName()靜態(tài)方法才會(huì)導(dǎo)致強(qiáng)制初始化該類

類加載器

類加載器負(fù)責(zé)將.class 文件加載到內(nèi)存中,并為之生成對(duì)應(yīng)的java.lang.Class 對(duì)象

1. 類加載器簡介

類加載器負(fù)責(zé)加載所有的類,系統(tǒng)為所有被載入內(nèi)存中的類生成一個(gè)java.lang.Class 實(shí)例

當(dāng)JVM啟動(dòng)時(shí),會(huì)形成由三個(gè)類加載器組成的初始類加載器層次結(jié)構(gòu)

  • Bootstrap ClassLoader:根類加載器
  • Extension ClassLoader:擴(kuò)展類加載器
  • System ClassLoader:系統(tǒng)類加載器

例子:獲取根類加載器所加載的核心類庫

package com.example;

import java.net.URL;

/**
 * Created by zhoujian on 2017/4/3.
 */

public class Text
{
    public static void main(String[] args)
    {
        //獲取根類加載器所加載的全部URL數(shù)組
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++)
        {
            //輸出根類加載器的全部url
            System.out.println(urls[i].toExternalForm());
        }
    }
}

Snip20170404_1.png

Extension ClassLoader:擴(kuò)展類加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(%JAVA_HOME%jre/lib/ext或者由java.ext.dirs系統(tǒng)屬性指定的目錄)中JAR包的類

System ClassLoader:系統(tǒng)類加載器,可以通過ClassLoader的靜態(tài)方法 getSystemClassLoader()來獲取系統(tǒng)類加載器

2. 類加載機(jī)制

JVM的類加載機(jī)制主要要三種:

  • 全盤負(fù)責(zé) :就是當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí),該Class所依賴的和引用的其它Class也將由該類加載器負(fù)責(zé)載入
  • 父類委托:先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時(shí)才嘗試從自己的類路徑中加載該類
  • 緩存機(jī)制:所有加載過得Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從緩存中獲取,只要緩存中不存在該Class對(duì)象時(shí),系統(tǒng)才會(huì)讀取對(duì)應(yīng)的二進(jìn)制數(shù)據(jù),并轉(zhuǎn)換成Class對(duì)象,存入緩存中。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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