1. 程式人生 > >對static關鍵字的理解

對static關鍵字的理解

static關鍵字修飾的方法和變數是全域性唯一的,不會因為new而新建立一個,jvm類載入的時,為靜態的提前初始化:

Class檔案中的“類”從載入到JVM記憶體中,到卸載出記憶體過程有七個生命週期階段。類載入機制包括了前五個階段。

如下圖所示:

其中,載入、驗證、準備、初始化、解除安裝的開始順序是確定的,注意,只是按順序開始,進行與結束的順序並不一定。解析階段可能在初始化之後開始。

另外,類載入無需等到程式中“首次使用”的時候才開始,JVM預先載入某些類也是被允許的。(類載入的時機

一、類的載入

我們平常說的載入大多不是指的類載入機制,只是類載入機制中的第一步載入。在這個階段,JVM主要完成三件事:

1、通過一個類的全限定名(包名與類名)來獲取定義此類的二進位制位元組流(Class檔案)。而獲取的方式,可以通過jar包、war包、網路中獲取、JSP檔案生成等方式。

2、將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。這裡只是轉化了資料結構,並未合併資料。(方法區就是用來存放已被載入的類資訊,常量,靜態變數,編譯後的程式碼的執行時記憶體區域)

3、在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口。這個Class物件並沒有規定是在Java堆記憶體中,它比較特殊,雖為物件,但存放在方法區中。

二、類的連線

類的載入過程後生成了類的java.lang.Class物件,接著會進入連線階段,連線階段負責將類的二進位制資料合併入JRE(Java執行時環境)中。類的連線大致分三個階段。

1、驗證:驗證被載入後的類是否有正確的結構,類資料是否會符合虛擬機器的要求,確保不會危害虛擬機器安全。

2、準備:為類的靜態變數(static filed)在方法區分配記憶體,並賦預設初值(0值或null值)。如static int a = 100;

靜態變數a就會在準備階段被賦預設值0。

對於一般的成員變數是在類例項化時候,隨物件一起分配在堆記憶體中。

另外,靜態常量(static final filed)會在準備階段賦程式設定的初值,如static final int a = 666;  靜態常量a就會在準備階段被直接賦值為666,對於靜態變數,這個操作是在初始化階段進行的。

3、解析:

將類的二進位制資料中的符號引用換為直接引用。

三、類的初始化

類初始化是類載入的最後一步,除了載入階段,使用者可以通過自定義的類載入器參與,其他階段都完全由虛擬機器主導和控制。到了初始化階段才真正執行Java程式碼。

類的初始化的主要工作是為靜態變數賦程式設定的初值。

如static int a = 100;在準備階段,a被賦預設值0,在初始化階段就會被賦值為100。

Java虛擬機器規範中嚴格規定了有且只有五種情況必須對類進行初始化

1、使用new位元組碼指令建立類的例項,或者使用getstatic、putstatic讀取或設定一個靜態欄位的值(放入常量池中的常量除外),或者呼叫一個靜態方法的時候,對應類必須進行過初始化。

2、通過java.lang.reflect包的方法對類進行反射呼叫的時候,如果類沒有進行過初始化,則要首先進行初始化。

3、當初始化一個類的時候,如果發現其父類沒有進行過初始化,則首先觸發父類初始化。

4、當虛擬機器啟動時,使用者需要指定一個主類(包含main()方法的類),虛擬機器會首先初始化這個類。

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

注意,虛擬機器規範使用了“有且只有”這個詞描述,這五種情況被稱為“主動引用”,除了這五種情況,所有其他的類引用方式都不會觸發類初始化,被稱為“被動引用”。