類載入機制與反射__java

來源:互聯網
上載者:User

(一)類載入機制與反射

   Java類載入器除了根類載入器之外,其他類載入器都是使用Java語言編寫的,所以程式員完全可以開發自己的類載入器,通過使用自訂類載入器,可以完成一些特定的功能。

 

(二)類的載入、串連和初始化

  系統可能在第一次使用某個類時載入該類,也可能採用預先載入機制來載入某個類。

  1.  JVM和類  

  當我們調用Java命令運行某個Java程式時,該命令將會啟動一個Java虛擬機器進程,不管該Java程式有多麼複雜,該程式啟動了多少個線程,它們都處於該Java虛擬機器進程裡。

  同一個JVM的所有線程、所有變數都處於同一個進程裡,它們都使用該JVM進程的記憶體區。

  當系統出現以下幾種情況時,JVM進程將被終止:

    (1)程式運行到最後正常結束。

    (2)程式運行到使用System.exit()或Runtime.getRuntime().exit()代碼處結束程式。

    (3)程式執行過程中遇到未捕獲的異常或錯誤而結束。

    (4)程式所在平台強制結束了JVM進行。

 

下面舉例說明:不同JVM程式的運行情況:

public class A{    //定義該類的靜態Field    public static int a = 6;}

 

public class ATest1{    public static void main(String[] args)    {        //建立A類的執行個體        A a = new A();                //讓a執行個體的a Field值自增        a.a++;                System.out.println(a.a);    }}

 

 運行結果:7

 

public class Atest2{    public static void main(String[] args)    {        //建立A類執行個體        A b = new A();                //輸出b執行個體的a Field值        System.out.println(b.a);    }}

 

運行結果:6

 

 

因為運行ATest1和ATest2是兩次運行JVM進程,第一次運行JVM結束後,它對A類所做的修改將全部丟失---第二次運行JVM時將再次初始化A類。

 

   2.  類的載入

    當程式主動使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入、串連、初始化3個步驟來對該類進行初始化。

    類載入指的是將類的class檔案讀入記憶體,並為之建立一個java.lang.Class對象,也就是說,當程式中使用任何類時,系統都會為之建立一個java.lang.Class對象。

    類的載入由類載入器完成,類載入器通常由JVM提供,JVM提供的這些類載入器通常被稱為系統類別載入器。除此之外,開發人員還可以通過繼承ClassLoader基類來建立自己的類載入器。

  通過使用不同的類載入器,可以從不同來源載入類的位元據:

  1.從本地系統載入class檔案。

  2.從JAR包載入class檔案

  3.通過網路載入class檔案

  4.把一個Java源檔案動態編譯,並執行載入

( 類載入器通常無須等到“首次使用”該類時才載入該類,Java虛擬機器規範允許系統積極式載入某些類。)

 

   3.  類的串連

  當類被載入之後,系統為之產生一個對應的Class對象,接著將會進入串連階段,串連階段負責把類的位元據合并到JRE中。

類串連又可分為如下3個階段:

(1)驗證:驗證階段用於檢驗被載入的類是否有正確的內部結構,並和其他類協調一致。

(2)準備:類準備階段則負責為類的靜態Field分配記憶體,並設定預設初始值。

(3)解析:將類的位元據中的符號引用替換成直接引用。

 

   4.  類的初始化

  在類的初始化階段,虛擬機器負責對類進行初始化,主要就是對靜態Field進行初始化。在Java類中對靜態Field指定初始值有兩種方式:

  (1)聲明靜態Field時指定初始值

  (2)使用靜態初始化塊為靜態Field指定初始值  

public class Test{    //聲明a變數時指定初始值    static int a = 5;    static int b;    static int c;        static    {        //使用靜態初始化塊b變數指定初始值        b = 6;    }}

 

 

 

  對於上面代碼,程式為靜態Field a、b都顯示指定了初始值,所以這兩個靜態Field的值分別為5、6,但靜態Field c則沒有指定初始值,它將採用預設初始值0

 

 

  JVM初始化一個類包含如下幾個步驟:

  (1)假如這個類還沒有被載入和串連,則程式先載入並串連該類。

  (2)假如該類的直接父類還沒有被初始化,則先初始化其直接父類。

  (3)假如類中有初始化語句,則系統依次執行這些初始化語句。

  (注意:當執行第2個步驟時,系統對直接父類的初始化步驟也遵循此步驟1-3;如果該直接父類又有直接父類,則系統再次重複這3個步驟來先初始化這個父類....依次類推,所以JVM最先初始化的總是java.lang.Object類。當程式主動使用任何一個類時,系統會保證該類以及所有父類都會被初始化)

 

 

 (三)類初始化時機

  當Java程式首次通過下面6種方式來使用某個類或介面時,系統就會初始化該類或介面:

(1)建立類的執行個體。為某個類建立執行個體的方法包括:使用new操作符來建立執行個體,通過反射來建立執行個體,通過還原序列化的方式來建立執行個體。

(2)調用某個類的靜態方法

(3)訪問某個類或介面的靜態Field,或為該靜態Filed賦值。

(4)使用反射方式來強制建立某個類或介面對應的java.lang.Class對象。

(5)初始化某個類的子類。當初始化某個類的子類時,該子類的所有父類都會初始化。

(6)直接使用java.exe命令來運行某個主類。

 

   對於一個final型的靜態Field,如果該Field的值在編譯時間就可以確定下來,那麼這個Field相當於"宏變數"。Java編譯器會在編譯時間直接把這個Field出現的地方替換成它的值,因此即使程式使用該靜態Filed,也不會導致該類的初始化。

  下面舉例:

package Chapter18;public class CompileConstantTest{    public static void main(String[] args)    {        //訪問、輸出MyTest中的compileConstant Field        System.out.println(MyTest.compileConstant);  //1處    }}class MyTest{    static    {        System.out.println("靜態初始化塊...");    }        //使用一個字串直接量為static final Field賦值    static final String compileConstant = "瘋狂Java講義";}

  運行結果:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.