Java實現類加載機制簡介

類的加載機制

兩次運行的Java程序爲什麼不能共享數據?

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

同一個JVM的所有線程、所有變量都處於同一個進程裏,它們都使用該JVM進程的內存區。

兩次運行Java程序處於兩個不同的JVM進程中,兩個JVM之間並不會共享數據。

類加載機制

當程序主動使用某個類時,如果該類還未被加載到內存中,系統會通過加載、連接、初始化三個步驟來對該類進行初始化,如果沒有意外,JVM將會連續完成這三個步驟,所以有時也把這三個步驟統稱爲類加載或類初始化。

類加載

類加載指的是將類的class文件讀入內存,併爲之創建一一個 java.lang.Class對象,也就是說當程序中使用任何類時,系統都會爲之建立-一個java.lang.Class對象

類是某一類對象的抽象,是概念層次的東西。

類的連接

當類被加載之後,系統爲之生成一-個對應的Class對象,接着將會進入連接階段,連接階段將會負責把類的二進制數據合併到JRE中。類連接又可分爲如下三個階段:
驗證:驗證階段用於檢驗被加載的類是否有正確的內部結構,並和其他類協調一-致。
準備:類準備階段則負責爲類的靜態屬性分配內存,並設置默認初始值。.
解析:將類的二進制數據中的符號引用替換成直接引用。

類的初始化

在類的初始化階段,虛擬機負責對類進行初始化,主要就是對靜態屬性進行初始化。

在Java類中對靜態屬性指定初始值有兩種方式:

(1)聲明靜態屬性時指定初始值;

(2)使用靜態初始化塊爲靜態屬性指定初始值。

類的初始化時機(什麼時候會進行類的初始化)?

當Java程序首次通過下面6種方式來使用某個類或接口時,系統就會初始化該類或接口:
創建類的實例。爲某個類創建實例的方式包括使用new操作符來創建實例,通過反射來創建實例,通過反序列化的方式來創建實例。
調用某個類的靜態方法。
訪問某個類或接 口的靜態屬性,或爲該靜態屬性賦值。
使用反射方式來強制創建某個類或接口對應的ja.lang.Class對象。例如代碼:Class.forName(“Person”),如果系統還未初始化Person類,則這行代碼將會導致該Person類被初始化,並返回Person對應的java.lang.Class對象。
初始化某個類的子類, 當初始化某個類的子類時,該子類的所有父類都會被初始化。
直接使用 java.exe命令來運行某個主類,當運行某個主類時,程序會先初始化該主類。

除此之外,下面有幾種情形需要特別指出:
對於一個final型的靜態屬性,如果該屬性可以在編譯時就得到屬性值,則可認爲該屬性可被當成編譯時常量。當程序使用編譯時常量時,系統會認爲這是對該類的被動使用,所以不會導致該類的初始化。

使用final修飾符修飾的成員變量,在類調用時,是否初始化此類?

當某個靜態屬性使用final修飾,而且它的值可以在編譯時得到,那麼程序其他地方使用該靜態屬性時,實際上並不會使用該靜態屬性,而是相當於使用常量。

例如:

static final String str = "Java學習";

str的值在編譯器就確定是"Java學習",所以使用該靜態屬性,不會初始化此類。

反之,如果final類型的靜態屬性的值不能在編譯時得到,必須等到運行時纔可以確定該屬性的值**,如果通過該類來訪問該靜態屬性,則可以認爲是主動訪問使用該類,將會導致該類被初始化。

例如:

static final String str = "現在的系統時間是:" + System.currentTimeMillis();

str的值在編譯期無法確定,因爲要在運行時獲取當前系統時間,所以使用該靜態屬性,會初始化此類。

注意:

當使用ClassLoader類loadClass()方法來加載某個類時,該方法只是加載該類,並不會執行該類的初始化。當使用ClassforName()靜態方法纔會導致強制初始化該類

圖解類加載過程

當程序主動使用某個類時,如果該類還未被加載到內存中,則系統會通過如下三個步驟來對該類進行初始化。

在這裏插入圖片描述

類加載器

類裝載器負責加載所有的類,系統爲所有被載入內存中的類生成一個java.lang.Class 實例。

當JVM啓動時,會形成由三個類加載器組成的初始類加載器層次結構:
Bootstrap Classl oader:根類(引導或原始)加載器。它負責加載Java的核心類。根類加載器非常特殊,它並不是java.lang.ClassLoader的子類,而是由JVM自身實現的。
Extension ClassL oader:擴展類加載器。它負貴加載JRE的擴展月錄(JAVA_ HOME/jre/lib/ext或者山java.ext.dirs 系統屬性指定的幾錄)中JAR的類包。通過這種方式,我們就可以爲Java護展核心類以外的新功能,只要我們把自己開發的類打包成JAR文件,然後放入JAVA_ HOME/jre/lib/ext 路徑即可。
System ClassLoader:系統類加載器。它負責在JVM啓動時,加載來自命令java中的Classpath選項或java.class path系統屬性,或CLASSPATH環境變量所指定的JAR包和類路徑。

程序可以通過ClassLoader靜 態方法getSystemClassLoader()獲取 該類加載器。如果沒有特別指定,則用戶自定義的類加載器都以該類加載器作爲它的父加載器。

思考:一個類被載入JVM中,同一個類就不會被再次載入了。怎麼樣纔算“同一個類”?

在JVM中,一個類用其全限定類名和其類加載器作爲其唯一標識。

類加載器的加載機制

JVM的類加載機制主要有如下三種機制:

  1. 全盤負責:所謂全盤負責,就是說當一個類加載器負責加載某個Class的時候,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯式使用另外一個類加載器來載入。
  2. 父類委託:所謂父類委託則是先讓parent (父)類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類。
  3. 緩存機制:緩存機制將會保證所有被加載過的Class 都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存中搜尋該Class,只有當緩存中不存在該Class對象時,系統纔會重讀取該類對應的二進制數據,並將其轉換成Class對象,並存入cache。這就是爲什麼我們修改了Class 後,程序必須重新啓動JVM,程序所作的修改纔會生效的原因。

思考:爲什麼我們修改了Class 後,程序必須重新啓動JVM,程序所作的修改纔會生效?

緩存機制將會保證所有被加載過的Class 都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存中搜尋該Class,只有當緩存中不存在該Class對象時,系統纔會重讀取該類對應的二進制數據,並將其轉換成Class對象,並存入cache。

類加載器的作用

將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時數據結構,然後在堆中生成一個代表這個類的java.lang.Class對象,作爲方法區中類數據的訪問入口。

自定義類加載器

JVM中除根加載器之外的所有炎加載器都是ClassLoader 子類的實例,開發者可以通過擴展ClassLoader的子類,並重寫該ClassLoader所包含的方法來實現自定義的類加載器。

如何實現,暫略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章