1. 程式人生 > >類初始化與類物件初始化

類初始化與類物件初始化

類的生命週期:

載入——> 驗證、準備、解析——>初始化——>使用——>解除安裝

類初始化進行的5種情況:

1.遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令的最常見的java程式碼場景是:使用new關鍵字例項化物件的時候、讀取或者設定一個類的靜態欄位(被final修飾的除外),以及呼叫一個類的靜態方法

2.使用java.lang.reflect包的方法對類進行反射呼叫的時候,如果類沒有初始化過,則需要先觸發其初始化

3.當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化

4.當虛擬機器啟動時,虛擬機器初始化一個main方法的類

5.當使用jdk1.7的動態語言支援時,如果一個java.lang.invoke.MethodHandler例項後的解析結果REF_getStatic、REF_putstaic、REF_involeStatic的方法控制代碼,並且這個方法控制代碼所對應的類沒有進行初始化,則需要先觸發其初始化

類初始化階段是執行類構造器<clinit>()方法的過程。

<clinit>()方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊(static{}塊)中的語句合併產生的,編譯器收集的順序是由語句在原始檔中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句之前的變數,定義在它之後的變數,在前面的靜態語句塊可以賦值,但是不能訪問。

<clinit>方法與類的建構函式(或者說例項構造器<init>()方法)不同,它不需要顯示得呼叫父類構造器,虛擬機器會保證在子類的<clinit>()方法執行之前,父類的<clinit>()方法已經執行完畢。

由於父類的<clinit>()方法先執行,也就意味著父類中定義的靜態語句塊要優先於子類的變數賦值操作。

<clinit>()方法對於類或介面來說並不是必需的,如果一個類中沒有靜態語句塊,也沒有類變數,那麼編譯器可以不為這個類生產<clinit>()方法

介面中不鞥使用靜態語句塊,但仍然有變數初始化的賦值操作,因此介面與類一樣都會生成<clinit>()方法,但介面與類不同的是,執行介面的<clinit>()方法不需要先執行父介面的<clinit>()方法。只有當父介面中定義的變數使用時,父接口才會初始化。另外,介面的實現類在初始化時也一樣不會執行介面的<clinit>()方法。

虛擬機器會保證一個類的<clinit>()方法在多執行緒環境中被正確地加鎖、同步,如果多個執行緒同時去初始化一個類,哪兒只會有一個執行緒去執行這個類的<clinit>方法,其他執行緒都需要阻塞等待,知道活動執行緒執行clinit()方法完畢。如果一個類的<clinit>()方法有耗時很長的操作,就可能造成多一個程序阻塞。